<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.0.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/wang-cheng/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/wang-cheng/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/wang-cheng/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/wang-cheng/images/logo.svg" color="#222">

<link rel="stylesheet" href="/wang-cheng/css/main.css">


<link rel="stylesheet" href="/wang-cheng/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"cheng-wang123.gitee.io","root":"/wang-cheng/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="线程的常用方法currentThread()方法 该方法可以获取当前线程，该方法的返回值是在代码实际运行时候的线程对象。 setName()&#x2F;getName()方法 设置线程名称&#x2F;返回线程名称 isAlive()方法 布尔类型，判断当前线程是否处于活动状态，活动状态就是线程已启动并且尚未终止 sleep()方法 Thread.sleep(millis); 让当前线程休眠指定的毫秒数 getId()">
<meta property="og:type" content="article">
<meta property="og:title" content="多线程">
<meta property="og:url" content="https://cheng-wang123.gitee.io/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/index.html">
<meta property="og:site_name" content="诚の博客">
<meta property="og:description" content="线程的常用方法currentThread()方法 该方法可以获取当前线程，该方法的返回值是在代码实际运行时候的线程对象。 setName()&#x2F;getName()方法 设置线程名称&#x2F;返回线程名称 isAlive()方法 布尔类型，判断当前线程是否处于活动状态，活动状态就是线程已启动并且尚未终止 sleep()方法 Thread.sleep(millis); 让当前线程休眠指定的毫秒数 getId()">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://cheng-wang123.gitee.io/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/CAS%E5%8E%9F%E7%90%86.png">
<meta property="og:image" content="https://cheng-wang123.gitee.io/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/%E7%BA%BF%E7%A8%8B%E6%B1%A0.png">
<meta property="og:image" content="https://cheng-wang123.gitee.io/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/Excutor%E6%A1%86%E6%9E%B6.png">
<meta property="og:image" content="https://cheng-wang123.gitee.io/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/ForkJoinPool.png">
<meta property="og:image" content="https://cheng-wang123.gitee.io/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/ThreadLocal.png">
<meta property="article:published_time" content="2022-04-10T01:21:12.000Z">
<meta property="article:modified_time" content="2022-04-13T00:40:26.637Z">
<meta property="article:author" content="王诚">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://cheng-wang123.gitee.io/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/CAS%E5%8E%9F%E7%90%86.png">

<link rel="canonical" href="https://cheng-wang123.gitee.io/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>多线程 | 诚の博客</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/wang-cheng/atom.xml" title="诚の博客" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/wang-cheng/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">诚の博客</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">记录学习中的点点滴滴</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/wang-cheng/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/wang-cheng/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/wang-cheng/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/wang-cheng/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          多线程
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-04-10 09:21:12" itemprop="dateCreated datePublished" datetime="2022-04-10T09:21:12+08:00">2022-04-10</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-04-13 08:40:26" itemprop="dateModified" datetime="2022-04-13T08:40:26+08:00">2022-04-13</time>
              </span>

          
            <span class="post-meta-item" title="阅读次数" id="busuanzi_container_page_pv" style="display: none;">
              <span class="post-meta-item-icon">
                <i class="fa fa-eye"></i>
              </span>
              <span class="post-meta-item-text">阅读次数：</span>
              <span id="busuanzi_value_page_pv"></span>
            </span>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h1 id="线程的常用方法"><a href="#线程的常用方法" class="headerlink" title="线程的常用方法"></a><strong>线程的常用方法</strong></h1><p><code>currentThread()方法</code></p>
<p>该方法可以获取当前线程，该方法的返回值是在代码实际运行时候的线程对象。</p>
<p><code>setName()/getName()方法</code></p>
<p>设置线程名称/返回线程名称</p>
<p><code>isAlive()方法</code></p>
<p>布尔类型，判断当前线程是否处于活动状态，活动状态就是线程已启动并且尚未终止</p>
<p><code>sleep()方法</code></p>
<p>Thread.sleep(millis); 让当前线程休眠指定的毫秒数</p>
<p><code>getId()方法</code></p>
<p>可以获得线程的唯一标识，某个编号的线程运行结束后,该编号可能被后续创建的线程使用。重启的 JVM 后,同一个线程的编号可能不一样</p>
<p><code>yield()方法</code></p>
<p>作用是放弃当前的 CPU 资源</p>
<p><code>setPriority()</code></p>
<p>thread.setPriority( num );设置线程的优先级,java 线程的优先级取值范围是 1 ~ 10 , 如果超出这个范围会抛出 异常 IllegalArgumentException。在操作系统中,优先级较高的线程获得 CPU 的资源越多；线程优先级本质上是只是给线程调度器一个提示信息,以便于调 度器决定先调度哪些线程. 注意不能保证优先级高的线程先运行.</p>
<p><code>interrupt()</code></p>
<p>中断线程。注意调用 interrupt()方法仅仅是在当前线程打一个停止标志,并不是真正的停止线程。是否中断该线程还需要当前线程自行判断。</p>
<p><code>setDaemon()</code></p>
<p>设置当前线程为守护线程</p>
<h1 id="多线程编程的优势与存在的风险"><a href="#多线程编程的优势与存在的风险" class="headerlink" title="多线程编程的优势与存在的风险"></a><strong>多线程编程的优势与存在的风险</strong></h1><p>多线程编程具有以下优势:</p>
<ol>
<li><p>提高系统的吞吐率(Throughout)：多线程编程可以使一个进程有多个并发(concurrent,即同时进行的)的操作</p>
</li>
<li><p>提高响应性(Responsiveness)：Web 服务器会采用一些专门的线 程负责用户的请求处理,缩短了用户的等待时间</p>
</li>
<li><p>充分利用多核(Multicore)处理器资源：通过多线程可以充分的 利用 CPU 资源</p>
</li>
</ol>
<p>多线程编程存在的问题与风险：</p>
<ol>
<li><p>线程安全(Thread safe)问题：多线程共享数据时,如果没有采取 正确的并发访问控制措施,就可能会产生数据一致性问题,如读取脏数 据(过期的数据), 如丢失数据更新</p>
</li>
<li><p>线程活性(thread liveness)问题：由于程序自身的缺陷或者由资 源稀缺性导致线程一直处于非 RUNNABLE 状态,这就是线程活性问题, 常见的活性故障有以下几种</p>
<ul>
<li><p>死锁(Deadlock)。 类似鹬蚌相争</p>
</li>
<li><p>锁死(Lockout), 类似于睡美人故事中王子挂了</p>
</li>
<li><p>活锁(Livelock)。类似于小猫咬自己尾巴</p>
</li>
<li><p>饥饿(Starvation)。类似于健壮的雏鸟总是从母鸟嘴中抢到食物.</p>
</li>
</ul>
</li>
<li><p>上下文切换(Context Switch)：处理器从执行一个线程切换到执 行另外一个线程</p>
</li>
<li><p>可靠性：可能会由一个线程导致JVM意外终止,其他的线程也无法执行.</p>
</li>
</ol>
<h1 id="线程安全问题"><a href="#线程安全问题" class="headerlink" title="线程安全问题"></a><strong>线程安全问题</strong></h1><p><strong>非线程安全主要是指多个线程对同一个对象的实例变量进行操作时,会出现值被更改,值不同步的情况。线程安全问题表现为三个方面: 原子性,可见性和有序性</strong></p>
<h2 id="原子性"><a href="#原子性" class="headerlink" title="原子性"></a><strong>原子性</strong></h2><p>原子(Atomic)就是不可分割的意思. 原子操作的不可分割有两层含义:</p>
<ol>
<li><p>访问(读,写)某个共享变量的操作从其他线程来看,该操作要 么已经执行完毕,要么尚未发生, 即其他线程年示到当前操作的中间结果</p>
</li>
<li><p>访问同一组共享变量的原子操作是不能够交错的</p>
</li>
</ol>
<h2 id="可见性"><a href="#可见性" class="headerlink" title="可见性"></a><strong>可见性</strong></h2><p>如果一个线程对共享变量更新后, 后续访问该变量的其他线程可 以读到更新的结果, 称这个线程对共享变量的更新对其他线程可见, 否则称这个线程对共享变量的更新对其他线程不可见</p>
<p>多线程程序因为可见性问题可能会导致其他线程读取到了旧数据(脏数据)</p>
<h2 id="有序性"><a href="#有序性" class="headerlink" title="有序性"></a><strong>有序性</strong></h2><p>有序性(Ordering)是指在什么情况下一个处理器上运行的一个线 程所执行的 内存访问操作在另外一个处理器运行的其他线程看来是乱序的(Out of Order)</p>
<h3 id="重排序"><a href="#重排序" class="headerlink" title="重排序"></a><strong>重排序</strong></h3><p>在多核处理器的环境下,编写的顺序结构,这种操作执行的顺序可能是没有保障的:</p>
<p>编译器可能会改变两个操作的先后顺序; </p>
<p>处理器也可能不会按照目标代码的顺序执行; </p>
<p>这种一个处理器上执行的多个操作,在其他处理器来看它的顺序与目标代码指定的顺序可能不一样,这种现象称为重排序.</p>
<h3 id="指令重排序"><a href="#指令重排序" class="headerlink" title="指令重排序"></a><strong>指令重排序</strong></h3><p>在源码顺序与程序顺序不一致,或者程序顺序与执行顺序不一致的情况下,我们就说发生了指令重排序(Instruction Reorder)。</p>
<p>javac 编译器一般不会执行指令重排序, 而 JIT 编译器可能执行指令重排序.处理器也可能执行指令重排序, 使得执行顺序与程序顺序不一致。</p>
<h3 id="保证内存访问的顺序性"><a href="#保证内存访问的顺序性" class="headerlink" title="保证内存访问的顺序性"></a><strong>保证内存访问的顺序性</strong></h3><p>可以使用 volatile 关键字, synchronized 关键字实现有序性</p>
<h2 id="线程同步"><a href="#线程同步" class="headerlink" title="线程同步"></a><strong>线程同步</strong></h2><p>Java平台提供的线程同步机制包括:锁,volatile关键字,final关键字,static关键字,以及相关的 API,如 Object.wait()/Object.notify()等</p>
<h2 id="锁概念"><a href="#锁概念" class="headerlink" title="锁概念"></a><strong>锁概念</strong></h2><p>锁(Lock)可以理解为对共享数据进行保护的一个许可证。 对于同一个许可证保护的共享数据来说,任何线程想要访问这些共享数据必须先持有该许可证. 一个线程只有在持有许可证的情况下才能对这些共享数据进行访问; 并且一个许可证一次只能被一个线程持有; 许可证线程在结束对共享数据的访问后必须释放其持有的许可证。</p>
<p>一线程在访问共享数据前必须先获得锁; 获得锁的线程称为锁的持有线程; 一个锁一次只能被一个线程持有. 锁的持有线程在获得锁之后和释放锁之前这段时间所执行的代码称为临界区(Critical Section)</p>
<p>锁具有排他性(Exclusive), 即一个锁一次只能被一个线程持有.这 种锁称为排它锁或互斥锁(Mutex).</p>
<p>JVM 把锁分为内部锁和显示锁两种. 内部锁通过 synchronized 关键字实现; 显示锁通过 java.concurrent.locks.Lock 接口的实现类实现的</p>
<h2 id="锁的作用"><a href="#锁的作用" class="headerlink" title="锁的作用"></a><strong>锁的作用</strong></h2><p>锁可以实现对共享数据的安全访问. 保障线程的原子性,可见性与有序性。</p>
<p>注意，使用锁保障线程的安全性,必须满足以下条件:</p>
<ol>
<li><p>这些线程在访问共享数据时必须使用同一个锁</p>
</li>
<li><p>即使是读取共享数据的线程也需要使用同步锁</p>
</li>
</ol>
<h2 id="锁的相关概念"><a href="#锁的相关概念" class="headerlink" title="锁的相关概念"></a><strong>锁的相关概念</strong></h2><p><strong>可重入性</strong>：一个线程持有该锁的时候能再次(多次)申请该锁。如果一个线程持有一个锁的时候还能够继续成功申请该锁,称该锁是可重入的, 否则就称该锁为不可重入的。</p>
<p><strong>锁的争用与调度</strong>：Java 平台中内部锁属于非公平锁, 显示 Lock 锁既支持公平锁又支 持非公平锁</p>
<p><strong>锁的粒度</strong>：一个锁可以保护的共享数据的数量大小称为锁的粒度。锁保护共享数据量大,称该锁的粒度粗, 否则就称该锁的粒度细。锁的粒度过粗会导致线程在申请锁时会进行不必要的等待.锁的 粒度过细会增加锁调度的开销。</p>
<h2 id="内部锁-synchronized-关键字"><a href="#内部锁-synchronized-关键字" class="headerlink" title="内部锁:synchronized 关键字"></a><strong>内部锁:synchronized 关键字</strong></h2><p>Java 中的每个对象都有一个与之关联的内部锁(Intrinsic lock)，这种锁也称为监视器(Monitor)，这种内部锁是一种排他锁，可以保障原子性，可见性与有序性。</p>
<p>内部锁是通过 synchronized 关键字实现的。synchronized 关键字修饰代码块,修饰该方法。</p>
<p>修饰代码块的语法:</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">synchronized</span><span class="params">( 对象锁 )</span></span> &#123;</span><br><span class="line">    同步代码块,可以在同步代码块中访问共享数据</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>修饰实例方法就称为同步实例方法、修饰静态方法称称为同步静态方法</p>
<h3 id="synchronized-同步代码块"><a href="#synchronized-同步代码块" class="headerlink" title="synchronized 同步代码块"></a><strong>synchronized 同步代码块</strong></h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test01</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">void</span> <span class="title function_">main</span>(<span class="params"><span class="built_in">String</span>[] args</span>) &#123;</span><br><span class="line">        <span class="comment">//创建两个线程,分别调用 mm()方法</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//先创建 Test01 对象,通过对象名调用 mm()方法</span></span><br><span class="line">        <span class="title class_">Test01</span> obj = <span class="keyword">new</span> <span class="title class_">Test01</span>();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">run</span>(<span class="params"></span>) &#123;</span><br><span class="line">                <span class="comment">//使用的锁对象this就是obj对象</span></span><br><span class="line">                <span class="comment">//谁调用，锁对象就是谁</span></span><br><span class="line">                obj.<span class="title function_">mm</span>();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).<span class="title function_">start</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">run</span>(<span class="params"></span>) &#123;</span><br><span class="line">                obj.<span class="title function_">mm</span>(); <span class="comment">//使用的锁对象this也是obj对象</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).<span class="title function_">start</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//定义方法,打印 100 行字符串</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">mm</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="comment">//经常使用this当前对象作为锁对象</span></span><br><span class="line">        synchronized (<span class="variable language_">this</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (int i = <span class="number">1</span>; i &lt;= <span class="number">100</span>; i++) &#123;</span><br><span class="line">                <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="title class_">Thread</span>.<span class="title function_">currentThread</span>().<span class="title function_">getName</span>() + <span class="string">&quot; --&gt; &quot;</span> + i);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果线程的锁不同，不能实现同步</p>
<p>想要同步必须使用同一个锁对象</p>
<p>类的成员变量是放在堆中的：new出来一个对象，都会保存到堆中，而堆中记录了这个对象的成员变量。相同类new出来的不同对象，成员变量互不干扰。</p>
<figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> Test02 &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">int</span> <span class="keyword">count</span> = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> main(String[] args) &#123;</span><br><span class="line">        <span class="comment">//先创建 Test01 对象</span></span><br><span class="line">        Test02 obj = <span class="keyword">new</span> Test02();</span><br><span class="line">        Test02 obj2 = <span class="keyword">new</span> Test02();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            @Override</span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> run() &#123;</span><br><span class="line">                <span class="comment">//obj.mm();//使用的锁对象this就是obj对象</span></span><br><span class="line">                <span class="keyword">synchronized</span> (obj) &#123; <span class="comment">//经常使用this当前对象作为锁对象</span></span><br><span class="line">                    <span class="keyword">while</span> (<span class="keyword">count</span> &gt; <span class="number">0</span>)&#123;</span><br><span class="line">                        System.out.<span class="keyword">println</span>(<span class="string">&quot;当前count为：&quot;</span>+<span class="keyword">count</span>--);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            @Override</span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> run() &#123;</span><br><span class="line">                <span class="comment">//obj2.mm(); //使用的锁对象 this 也是 obj2对象</span></span><br><span class="line">                <span class="keyword">synchronized</span> (obj2) &#123; <span class="comment">//经常使用this当前对象作为锁对象</span></span><br><span class="line">                    <span class="keyword">while</span> (<span class="keyword">count</span> &gt; <span class="number">0</span>)&#123;</span><br><span class="line">                        System.out.<span class="keyword">println</span>(<span class="string">&quot;当前count为：&quot;</span>+<span class="keyword">count</span>--);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>常量也可以作为锁对象<br><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test03</span> &#123;</span><br><span class="line">    int count = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">void</span> <span class="title function_">main</span>(<span class="params"><span class="built_in">String</span>[] args</span>) &#123;</span><br><span class="line">        <span class="comment">//创建两个线程,分别调用 mm()方法</span></span><br><span class="line">        <span class="comment">//先创建 Test01 对象,通过对象名调用 mm()方法</span></span><br><span class="line">        <span class="title class_">Test03</span> obj = <span class="keyword">new</span> <span class="title class_">Test03</span>();</span><br><span class="line">        <span class="title class_">Test03</span> obj2 = <span class="keyword">new</span> <span class="title class_">Test03</span>();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">run</span>(<span class="params"></span>) &#123;</span><br><span class="line">                obj.<span class="title function_">mm</span>(); <span class="comment">//使用的锁对象 OBJ 常量</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).<span class="title function_">start</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">run</span>(<span class="params"></span>) &#123;</span><br><span class="line">                obj2.<span class="title function_">mm</span>(); <span class="comment">//使用的锁对象 OBJ 常量</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).<span class="title function_">start</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> final <span class="title class_">Object</span> <span class="variable constant_">OBJ</span> = <span class="keyword">new</span> <span class="title class_">Object</span>();<span class="comment">//定义一个常量</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//定义方法,打印 100 行字符串</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">mm</span>(<span class="params"></span>) &#123;</span><br><span class="line">        synchronized (<span class="variable constant_">OBJ</span>) &#123; <span class="comment">//使用一个常量对象作为锁对象</span></span><br><span class="line">            <span class="keyword">while</span> (count &gt; <span class="number">0</span>)&#123;</span><br><span class="line">                <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="string">&quot;当前count = &quot;</span> +count--);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>把整个方法体作为同步代码块<br><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">默认的锁对象是当前类的运行时类对象</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test06</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span> &#123;</span><br><span class="line">        <span class="comment">//先创建 Test01 对象,通过对象名调用 mm()方法</span></span><br><span class="line">        Test06 obj = <span class="keyword">new</span> Test06();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//一个线程调用 m1()方法</span></span><br><span class="line">        <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            @Override</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">                obj.m1(); <span class="comment">//使用的锁对象是 Test06.class</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//另一个线程调用 sm2()方法</span></span><br><span class="line">        <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            @Override</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">                Test06.sm2(); <span class="comment">//使用的锁对象是 Test06.class</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//定义方法,打印 100 行字符串</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">m1</span>()</span>&#123;</span><br><span class="line">        <span class="comment">//使用当前类的运行时类对象作为锁对象,可以简单的理解为把 Test06 类的字节码文件作为锁对象</span></span><br><span class="line">        synchronized ( Test06.<span class="keyword">class</span> ) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">100</span>; i++) &#123;</span><br><span class="line">                System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot; --&gt; &quot;</span> + i);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//使用 synchronized 修饰静态方法,同步静态方法, 默认运行时类Test06.class 作为锁对象</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> synchronized <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sm2</span>()</span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">100</span>; i++) &#123;</span><br><span class="line">            System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot; --&gt; &quot;</span> + i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>同步方法锁的粒度粗, 执行效率低, 同步代码块执行效率高</p>
<h2 id="脏读"><a href="#脏读" class="headerlink" title="脏读"></a><strong>脏读</strong></h2><p>出现读取属性值出现了一些意外, 读取的是中间值,而不是修改之后的值。出现脏读的原因是对共享数据的修改与对共享数据的读取不同步</p>
<p>解决方法:</p>
<p>不仅对修改数据的代码块进行同步,还要对读取数据的代码块同步。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test08</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="comment">//开启子线程设置用户名和密码</span></span><br><span class="line">        <span class="type">PublicValue</span> <span class="variable">publicValue</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PublicValue</span>();</span><br><span class="line">        <span class="type">SubThread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SubThread</span>(publicValue);</span><br><span class="line">        t1.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//为了确定设置成功</span></span><br><span class="line">        Thread.sleep(<span class="number">100</span>);</span><br><span class="line">        <span class="comment">//在 main 线程中读取用户名,密码</span></span><br><span class="line">        publicValue.getValue();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//定义线程,设置用户名和密码</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SubThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> PublicValue publicValue;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">SubThread</span><span class="params">( PublicValue publicValue)</span>&#123;</span><br><span class="line">            <span class="built_in">this</span>.publicValue = publicValue;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            publicValue.setValue(<span class="string">&quot;bjpowernode&quot;</span>, <span class="string">&quot;123&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">PublicValue</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">&quot;wkcto&quot;</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="type">String</span> <span class="variable">pwd</span> <span class="operator">=</span> <span class="string">&quot;666&quot;</span>;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">getValue</span><span class="params">()</span>&#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;,getter -- name: &quot;</span> + name + <span class="string">&quot;,--pwd: &quot;</span> + pwd);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">setValue</span><span class="params">(String name, String pwd)</span>&#123;</span><br><span class="line">            <span class="built_in">this</span>.name = name;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(<span class="number">1000</span>); <span class="comment">//模拟操作name 属性需要一定时间</span></span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">this</span>.pwd = pwd;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;,setter --name:&quot;</span> + name + <span class="string">&quot;, --pwd: &quot;</span> + pwd );</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>线程出现异常会自动释放锁</strong></p>
<h2 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a><strong>死锁</strong></h2><p>在多线程程序中,同步时可能需要使用多个锁,如果获得锁的顺序不一致,可能会导致死锁</p>
<p>如何避免死锁?</p>
<p>当需要获得多个锁时,所有线程获得锁的顺序保持一致即可。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test10</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">SubThread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SubThread</span>();</span><br><span class="line">        t1.setName(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">        t1.start();</span><br><span class="line">        <span class="type">SubThread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SubThread</span>();</span><br><span class="line">        t2.setName(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">        t2.start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SubThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">lock1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">lock2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="string">&quot;a&quot;</span>.equals(Thread.currentThread().getName())) &#123;</span><br><span class="line">                <span class="keyword">synchronized</span> (lock1) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;a 线程获得了 lock1 锁,还需 要获得 lock2 锁&quot;</span>);</span><br><span class="line">                    <span class="keyword">synchronized</span> (lock2) &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;a 线程获得 lock1 后又 获得了 lock2,可以想干任何想干的事&quot;</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (<span class="string">&quot;b&quot;</span>.equals(Thread.currentThread().getName())) &#123;</span><br><span class="line">                <span class="keyword">synchronized</span> (lock2) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;b 线程获得了 lock2 锁,还需要获得 lock1 锁&quot;</span>);</span><br><span class="line">                    <span class="keyword">synchronized</span> (lock1) &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;b 线程获得lock2后又 获得了 lock1,可以想干任何想干的事&quot;</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>获得锁的顺序不同，可能导致死锁的发生。</p>
<h1 id="轻量级同步机制-volative-关键字"><a href="#轻量级同步机制-volative-关键字" class="headerlink" title="轻量级同步机制:volative 关键字"></a><strong>轻量级同步机制:volative 关键字</strong></h1><h2 id="volatile-的作用"><a href="#volatile-的作用" class="headerlink" title="volatile 的作用"></a><strong>volatile 的作用</strong></h2><p>volatile 关键的作用使变量在多个线程之间可见</p>
<p>volatile 的作用可以强制线程从公共内存中读取变量的值,而不是从工作内存中读取。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test02</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">void</span> <span class="title function_">main</span>(<span class="params"><span class="built_in">String</span>[] args</span>) &#123;</span><br><span class="line">        <span class="comment">//创建 PrintString 对象</span></span><br><span class="line">        <span class="title class_">PrintString</span> printString = <span class="keyword">new</span> <span class="title class_">PrintString</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//开启子线程,让子线程执行 printString 对象的 printStringMethod()方法</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">run</span>(<span class="params"></span>) &#123;</span><br><span class="line">                printString.<span class="title function_">printStringMethod</span>();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).<span class="title function_">start</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//main 线程睡眠 1000 毫秒</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="title class_">Thread</span>.<span class="title function_">sleep</span>(<span class="number">1000</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (<span class="title class_">InterruptedException</span> e) &#123;</span><br><span class="line">            e.<span class="title function_">printStackTrace</span>();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="string">&quot;在 main 线程中修改打印标志&quot;</span>);</span><br><span class="line">        printString.<span class="title function_">setContinuePrint</span>(<span class="literal">false</span>);</span><br><span class="line">        <span class="comment">//程序运行,查看在 main 线程中修改了打印标志之后 ,子线程打印是否可以结束打印</span></span><br><span class="line">        <span class="comment">//程序运行后, 可能会出现死循环情况</span></span><br><span class="line">        <span class="comment">//分析原因: main 线程修改了 printString 对象的打印标志后, 子线程读不到</span></span><br><span class="line">        <span class="comment">//解决办法: 使用 volatile 关键字修饰 printString 对象的打印标志.</span></span><br><span class="line">        <span class="comment">//volatile 的作用可以强制线程从公共内存中读取变量的值,而不是从工作内存中读取</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//定义类打印字符串</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">PrintString</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> volatile <span class="built_in">boolean</span> continuePrint = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title class_">PrintString</span> <span class="title function_">setContinuePrint</span>(<span class="params"><span class="built_in">boolean</span> continuePrint</span>) &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">continuePrint</span> = continuePrint;</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">printStringMethod</span>(<span class="params"></span>)&#123;</span><br><span class="line">            <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="title class_">Thread</span>.<span class="title function_">currentThread</span>().<span class="title function_">getName</span>() + <span class="string">&quot;开始....&quot;</span>);</span><br><span class="line">            <span class="keyword">while</span> (continuePrint)&#123;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="title class_">Thread</span>.<span class="title function_">currentThread</span>().<span class="title function_">getName</span>() + <span class="string">&quot;结束++++++++++++++&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果不用volatile修饰的话，该程序将陷入死循环。因为该线程是从工作内存读取continuePrint的值，而我们在其他线程中修改过continuePrint后，该线程看不到。</p>
<p><strong>volatile 与 synchronized 比较</strong></p>
<ol>
<li><p>volatile 关键字是线程同步的轻量级实现,所以volatile性能肯定 比 synchronized 要好; volatile 只能修饰变量,而 synchronized 可以修 饰方法,代码块. 随着 JDK 新版本的发布,synchronized 的执行效率也 有较大的提升,在开发中使用 sychronized 的比率还是很大的。</p>
</li>
<li><p>多线程访问 volatile 变量不会发生阻塞,而 synchronized 可能会阻塞</p>
</li>
<li><p>volatile 能保证数据的可见性,但是不能保证原子性;而 synchronized 可以保证原子性,也可以保证可见性</p>
</li>
<li><p>关键字 volatile 解决的是变量在多个线程之间的可见性;synchronized 关键字解决多个线程之间访问公共资源的同步性</p>
</li>
</ol>
<h2 id="volatile-非原子特性"><a href="#volatile-非原子特性" class="headerlink" title="volatile 非原子特性"></a><strong>volatile 非原子特性</strong></h2><p>volatile 关键字增加了实例变量在多个线程之间的可见性,但是不具备原子性</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test03</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span> &#123;</span><br><span class="line">        <span class="comment">//在 main 线程中创建 10 个子线程</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> MyThread().start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">MyThread</span> <span class="title">extends</span> <span class="title">Thread</span>&#123;</span><br><span class="line">        <span class="comment">//volatile 关键仅仅是表示所有线程从主内存读取 count 变量的值</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">volatile</span> <span class="keyword">static</span> <span class="built_in">int</span> count;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*这段代码运行后不是线程安全的,想要线程安全,需要使用 synchronized 进行同步,如果使用 synchronized 同时,也就不需要 volatile 关键了*/</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">addCount</span>()</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) &#123;</span><br><span class="line">                <span class="comment">//count++不是原子操作</span></span><br><span class="line">                count++;</span><br><span class="line">            &#125;</span><br><span class="line">            System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot; count=&quot;</span> + count);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line"><span class="comment">//        该方法安全</span></span><br><span class="line"><span class="comment">//        public synchronized static void addCount()&#123;</span></span><br><span class="line"><span class="comment">//            for (int i = 0; i &lt; 1000; i++) &#123;</span></span><br><span class="line"><span class="comment">//                //count++不是原子操作</span></span><br><span class="line"><span class="comment">//                count++;</span></span><br><span class="line"><span class="comment">//            &#125;</span></span><br><span class="line"><span class="comment">//            System.out.println(Thread.currentThread().getName() + &quot; count=&quot; + count);</span></span><br><span class="line"><span class="comment">//        &#125;</span></span><br><span class="line">        @Override</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">            addCount();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="常用原子类进行自增自减操作"><a href="#常用原子类进行自增自减操作" class="headerlink" title="常用原子类进行自增自减操作"></a><strong>常用原子类进行自增自减操作</strong></h2><p>我们知道 i++操作不是原子操作, 除了使用 Synchronized 进行同步 外,也可以使用 AtomicInteger/AtomicLong 原子类进行实现</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test04</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>) throws InterruptedException</span>&#123;</span><br><span class="line">        <span class="comment">//在 main 线程中创建 10 个子线程</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> MyThread().start();</span><br><span class="line">        &#125;</span><br><span class="line">        Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">        System.<span class="keyword">out</span>.println( MyThread.count.<span class="keyword">get</span>());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">MyThread</span> <span class="title">extends</span> <span class="title">Thread</span>&#123;</span><br><span class="line">        <span class="comment">//static int a = 0;</span></span><br><span class="line">        <span class="comment">//使用 AtomicInteger 对象</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> AtomicInteger count = <span class="keyword">new</span> AtomicInteger();</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">addCount</span>()</span>&#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) &#123;</span><br><span class="line">                <span class="comment">//自增的后缀形式</span></span><br><span class="line">                count.getAndIncrement();</span><br><span class="line">            &#125;</span><br><span class="line">            System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot; count=&quot;</span> + count.<span class="keyword">get</span>());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        </span><br><span class="line">        </span><br><span class="line"><span class="comment">//       该方法和下面的方法都是用的同步原理        </span></span><br><span class="line"><span class="comment">//        public static synchronized void addCount()&#123;</span></span><br><span class="line"><span class="comment">//            for (int i = 0; i &lt; 1000; i++) &#123;</span></span><br><span class="line"><span class="comment">//                //自增的后缀形式</span></span><br><span class="line"><span class="comment">//                a++;</span></span><br><span class="line"><span class="comment">//            &#125;</span></span><br><span class="line"><span class="comment">//            System.out.println(Thread.currentThread().getName() + &quot; a=&quot; + a);</span></span><br><span class="line"><span class="comment">//        &#125;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//        public synchronized static void addCount()&#123;</span></span><br><span class="line"><span class="comment">//            for (int i = 0; i &lt; 1000; i++) &#123;</span></span><br><span class="line"><span class="comment">//                //自增的后缀形式</span></span><br><span class="line"><span class="comment">//                count.getAndIncrement();</span></span><br><span class="line"><span class="comment">//            &#125;</span></span><br><span class="line"><span class="comment">//            System.out.println(Thread.currentThread().getName() + &quot; count=&quot; + count.get());</span></span><br><span class="line"><span class="comment">//        &#125;</span></span><br><span class="line"></span><br><span class="line">        @Override</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">            addCount();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="线程同步-1"><a href="#线程同步-1" class="headerlink" title="线程同步"></a><strong>线程同步</strong></h1><h2 id="CAS"><a href="#CAS" class="headerlink" title="CAS"></a><strong>CAS</strong></h2><p>CAS(Compare And Swap)是由硬件实现的</p>
<p>CAS 可以将 read- modify - write 这类的操作转换为原子操作</p>
<p>i++自增操作包括三个子操作: 从主内存读取 i 变量值 对 i 的值加 1 再把加 1 之后 的值保存到主内存</p>
<p>CAS 原理: 在把数据更新到主内存时,再次读取主内存变量的值,如 果现在变量的值与期望的值(操作起始时读取的值)一样就更新</p>
<img src="/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/CAS%E5%8E%9F%E7%90%86.png" class title="CAS原理">
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">CASTest</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span> &#123;</span><br><span class="line">        CASCounter casCounter = <span class="keyword">new</span> CASCounter();</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100000</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                @Override</span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">                    System.<span class="keyword">out</span>.println(casCounter.incrementAndGet());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title">CASCounter</span> &#123;</span><br><span class="line">    <span class="comment">//使用 volatile 修饰 value 值,使线程可见</span></span><br><span class="line">    <span class="keyword">volatile</span> <span class="keyword">private</span> <span class="built_in">long</span> <span class="keyword">value</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="built_in">long</span> <span class="title">getValue</span>()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">value</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//定义 comare and swap 方法</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> boolean <span class="title">compareAndSwap</span>(<span class="params"><span class="built_in">long</span> expectedValue, <span class="built_in">long</span> newValue</span>)</span> &#123;</span><br><span class="line">        <span class="comment">//如果当前 value 的值与期望的 expectedVAlue 值一样,就把当前的 Value 字段替换为 newValue 值</span></span><br><span class="line">        synchronized (<span class="keyword">this</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="keyword">value</span> == expectedValue) &#123;</span><br><span class="line">                <span class="keyword">value</span> = newValue;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//定义自增的方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="built_in">long</span> <span class="title">incrementAndGet</span>()</span>&#123;</span><br><span class="line">        <span class="built_in">long</span> oldvalue ;</span><br><span class="line">        <span class="built_in">long</span> newValue;</span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            oldvalue = <span class="keyword">value</span>;</span><br><span class="line">            newValue = oldvalue+<span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">while</span> (!compareAndSwap(oldvalue, newValue));</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> newValue;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>自己实现了一个CAS原理。用volatile从内存中读取数据，保证可见性。在自增方法中做自增操作以后，将自增后的值写入内存的方法用sychronized来实现，在该方法中检查内存中的值和预期中的值是否一致，不一致则撤销更新，一致则更新内存中的值。该操作可以保证多个线程同步执行，不会出现多线程同时写数据的情况。</p>
<p>CAS 实现原子操作背后有一个假设: 共享变量的当前值与当前线 程提供的期望值相同, 就认为这个变量没有被其他线程修改过。实际上这种假设不一定总是成立.如有共享变量 count = 0</p>
<p>A 线程对 count 值修改为 10</p>
<p>B 线程对 count 值修改为 20</p>
<p>C 线程对 count 值修改为 0</p>
<p>当前线程看到 count 变量的值现在是 0,现在是否认为 count 变量的值没有被其他线程更新呢? 这种结果是否能够接受?</p>
<p><strong>这就是 CAS 中的 ABA 问题,即共享变量经历了 A-&gt;B-&gt;A 的更新。</strong></p>
<p>是否能够接收 ABA 问题跟实现的算法有关。</p>
<p>如果想要规避 ABA 问题,可以为共享变量引入一个修订号(时间 戳), 每次修改共享变量时,相应的修订号就会增加 1. ABA 变量更 新过程变量: [A,0] -&gt;[B,1]-&gt;[A,2], 每次对共享变量的修改都会导致 修订号的增加,通过修订号依然可以准确判断变量是否被其他线程修改过。 <code>AtomicStampedReference</code> 类就是基于这种思想产生的</p>
<h2 id="原子变量类"><a href="#原子变量类" class="headerlink" title="原子变量类"></a><strong>原子变量类</strong></h2><p>原子变量类基于CAS实现的, 当对共享变量进行read-modify-write 更新操作时,通过原子变量类可以保障操作的原子性与可见性.对变量 的 read-modify-write 更新操作是指当前操作不是一个简单的赋值,而 是变量的新值依赖变量的旧值,如自增操作i++. 由于volatile只能保证 可见性,无法保障原子性, 原子变量类内部就是借助一个 Volatile 变量, 并且保障了该变量的 read-modify-write 操作的原子性, 有时把原子变 量类看作增强的 volatile 变量. 原子变量类有 12 个</p>
<ol>
<li>基础数据型：<ul>
<li>AtomicInteger</li>
<li>AtomicLong</li>
<li>AtomicBoolean</li>
</ul>
</li>
<li>数组型<ul>
<li>AtomicIntegerArray</li>
<li>AtomicLongArray</li>
<li>AtomicReferenceArray</li>
</ul>
</li>
<li>字段更新器<ul>
<li>AtomicIntegerFieldUpdater</li>
<li>AtomicLongFieldUpdater</li>
<li>AtomicReferenceFieldUpdater</li>
</ul>
</li>
<li>引用型<ul>
<li>AtomicReference</li>
<li>AtomicStampedReference</li>
<li>AtomicMarkableReference</li>
</ul>
</li>
</ol>
<p><strong>AtomicLong使用演示：</strong></p>
<p>使用原子变量类定义一个计数器,该计数器,在整个程序中都能使用,并且所有的地方都使用这一个计数器,这个计数器可以设计为单例</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Indicator</span> &#123;</span><br><span class="line">    <span class="comment">//构造方法私有化</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Indicator</span>()</span>&#123;&#125; <span class="comment">//定义一个私有的本类静态的对象</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> final Indicator INSTANCE = <span class="keyword">new</span> Indicator();</span><br><span class="line">    <span class="comment">//3)提供一个公共静态方法返回该类唯一实例</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Indicator <span class="title">getInstance</span>()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> INSTANCE;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//使用原子变量类保存请求总数,成功数,失败数</span></span><br><span class="line">    <span class="keyword">private</span> final AtomicLong requestCount = <span class="keyword">new</span> AtomicLong(<span class="number">0</span>); <span class="comment">//记录请求总数</span></span><br><span class="line">    <span class="keyword">private</span> final AtomicLong successCount = <span class="keyword">new</span> AtomicLong(<span class="number">0</span>); <span class="comment">//处理成功总数</span></span><br><span class="line">    <span class="keyword">private</span> final AtomicLong fialureCount = <span class="keyword">new</span> AtomicLong(<span class="number">0</span>); <span class="comment">//处理失败总数</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//有新的请求</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">newRequestReceive</span>()</span>&#123;</span><br><span class="line">        requestCount.incrementAndGet();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//处理成功</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">requestProcessSuccess</span>()</span>&#123;</span><br><span class="line">        successCount.incrementAndGet();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//处理失败</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">requestProcessFailure</span>()</span>&#123;</span><br><span class="line">        fialureCount.incrementAndGet();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//查看总数,成功数,失败数</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="built_in">long</span> <span class="title">getRequestCount</span>()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> requestCount.<span class="keyword">get</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="built_in">long</span> <span class="title">getSuccessCount</span>()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> successCount.<span class="keyword">get</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="built_in">long</span> <span class="title">getFailureCount</span>()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> fialureCount.<span class="keyword">get</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//模拟服务器的请求总数, 处理成功数,处理失败数</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span> &#123;</span><br><span class="line">        <span class="comment">//通过线程模拟请求,在实际应用中可以在 ServletFilter 中调用 Indicator 计数器的相关方法</span></span><br><span class="line">        <span class="comment">//模拟10000个线程对服务器发送请求</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10000</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                @Override</span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">                    <span class="comment">//每个线程就是一个请求,请求总数要加 1</span></span><br><span class="line">                    Indicator.getInstance().newRequestReceive();</span><br><span class="line">                    <span class="built_in">int</span> num = <span class="keyword">new</span> Random().nextInt();</span><br><span class="line">                    <span class="keyword">if</span> (num % <span class="number">2</span> == <span class="number">0</span>)&#123;</span><br><span class="line">                        <span class="comment">//偶数模拟成功</span></span><br><span class="line">                        Indicator.getInstance().requestProcessSuccess();</span><br><span class="line">                    &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">                        <span class="comment">//处理失败</span></span><br><span class="line">                        Indicator.getInstance().requestProcessFailure();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//打印结果</span></span><br><span class="line">        System.<span class="keyword">out</span>.println( Indicator.getInstance().getRequestCount()); <span class="comment">//总的请求数</span></span><br><span class="line">        System.<span class="keyword">out</span>.println( Indicator.getInstance().getSuccessCount()); <span class="comment">//成功数</span></span><br><span class="line">        System.<span class="keyword">out</span>.println( Indicator.getInstance().getFailureCount()); <span class="comment">//失败数</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>AtomicIntegerArray使用演示：</strong><br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test &#123;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) &#123; <span class="comment">//1)创建一个指定长度的原子数组</span></span><br><span class="line">        AtomicIntegerArray atomicIntegerArray = <span class="keyword">new</span> <span class="constructor">AtomicIntegerArray(10)</span>;</span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(atomicIntegerArray); <span class="comment">//[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]</span></span><br><span class="line">        <span class="comment">// 2)返回指定位置的元素</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.get(<span class="number">0</span>)); <span class="comment">//0</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.get(<span class="number">1</span>)); <span class="comment">//0</span></span><br><span class="line">        <span class="comment">// 3)设置指定位置的元素</span></span><br><span class="line">        atomicIntegerArray.set(<span class="number">0</span>, <span class="number">10</span>);</span><br><span class="line">        <span class="comment">//在设置数组元素的新值时, 同时返回数组元素的旧值</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.get<span class="constructor">AndSet(1, 11)</span> ); <span class="comment">//0</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray ); <span class="comment">//[10, 11, 0, 0, 0, 0, 0, 0, 0, 0]</span></span><br><span class="line">        <span class="comment">// 4)修改数组元素的值,把数组元素加上某个值,做加法运算</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.add<span class="constructor">AndGet(0, 22)</span> ); <span class="comment">//32   先做加法运算再返回该下标的值</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.get<span class="constructor">AndAdd(1, 33)</span>); <span class="comment">//11    先返回该下标的值后做加法运算</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray ); <span class="comment">//[32, 44, 0, 0, 0, 0, 0, 0, 0, 0]</span></span><br><span class="line">        <span class="comment">// 5)CAS 操作</span></span><br><span class="line">        <span class="comment">// 如果数组中索引值为 0 的元素的值是 32 , 就修改为 222</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.compare<span class="constructor">AndSet(0, 32, 222)</span>); <span class="comment">//true</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray ); <span class="comment">//[222, 44, 0, 0, 0, 0, 0, 0, 0, 0]</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.compare<span class="constructor">AndSet(1, 11, 333)</span>); <span class="comment">//false</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(atomicIntegerArray); </span><br><span class="line">        <span class="comment">// 6)自增/自减</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.increment<span class="constructor">AndGet(0)</span>); <span class="comment">//223, 相当于前缀  先自增后获取该下标的值</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.get<span class="constructor">AndIncrement(1)</span>); <span class="comment">//44, 相当于后缀  先获取该下标的值后自增</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray ); <span class="comment">//[223, 45, 0, 0, 0, 0, 0, 0, 0, 0]</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.decrement<span class="constructor">AndGet(2)</span>); <span class="comment">//-1             先自减后获取该下标的值</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray); <span class="comment">//[223, 45, -1, 0, 0, 0, 0, 0, 0, 0]</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray.get<span class="constructor">AndDecrement(3)</span>); <span class="comment">//0              先获取该下标的值后自减</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( atomicIntegerArray ); <span class="comment">//[223, 45, -1, -1, 0, 0, 0, 0, 0, 0] &#125;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>在多线程中使用 AtomicIntegerArray 原子数组:<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test02</span> &#123;</span><br><span class="line">    <span class="comment">//定义原子数组</span></span><br><span class="line">    <span class="type">static</span> AtomicIntegerArray atomicIntegerArray = <span class="keyword">new</span> <span class="built_in">AtomicIntegerArray</span>(<span class="number">10</span>);</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="type">static</span> <span class="type">void</span> <span class="title">main</span><span class="params">(<span class="type">String</span>[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//定义线程数组</span></span><br><span class="line">        Thread[] threads = <span class="keyword">new</span> Thread[<span class="number">10</span>];</span><br><span class="line">        <span class="comment">//给线程数组元素赋值</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; threads.length; i++) &#123;</span><br><span class="line">            threads[i] = <span class="keyword">new</span> <span class="built_in">AddThread</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//开启子线程</span></span><br><span class="line">        <span class="keyword">for</span> (Thread thread : threads) &#123;</span><br><span class="line">            thread.<span class="built_in">start</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//在主线程中查看自增完以后原子数组中的各个元素的值,在主线程中需要在所有子线程都执行完后再查看</span></span><br><span class="line">        <span class="comment">//把所有的子线程合并到当前主线程中</span></span><br><span class="line">        <span class="keyword">for</span> (Thread thread : threads) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                thread.<span class="built_in">join</span>();<span class="comment">//等待子线程执行完再往下执行</span></span><br><span class="line">            &#125; <span class="built_in">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.<span class="built_in">printStackTrace</span>();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.<span class="built_in">println</span>( atomicIntegerArray );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//定义一个线程类,在线程类中修改原子数组</span></span><br><span class="line">    <span class="type">static</span> <span class="keyword">class</span> <span class="title class_">AddThread</span> extends Thread&#123;</span><br><span class="line">        @<span class="function">Override</span></span><br><span class="line"><span class="function">        <span class="keyword">public</span> <span class="type">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="comment">//把原子数组的每个元素自增 1000 次</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; <span class="number">1000</span>; j++) &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; atomicIntegerArray.<span class="built_in">length</span>(); i++) &#123;</span><br><span class="line">                    atomicIntegerArray.<span class="built_in">getAndIncrement</span>(i % atomicIntegerArray.<span class="built_in">length</span>());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">/* for (int i = 0; i &lt; 10000; i++) &#123;</span></span><br><span class="line"><span class="comment">                    atomicIntegerArray.getAndIncrement(i % atomicIntegerArray.length());</span></span><br><span class="line"><span class="comment">                &#125;*/</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p><strong>AtomicIntegerFieldUpdater使用演示：</strong></p>
<p>AtomicIntegerFieldUpdater 可以对原子整数字段进行更新,要求：</p>
<ol>
<li>字符必须使用 volatile 修饰,使线程之间可见</li>
<li>只能是实例变量,不能是静态变量,也不能使用 final 修饰<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//要操作的类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">User</span> &#123;</span><br><span class="line">    <span class="built_in">int</span> id;</span><br><span class="line">    <span class="keyword">volatile</span> <span class="built_in">int</span> age;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">User</span>(<span class="params"><span class="built_in">int</span> id, <span class="built_in">int</span> age</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">this</span>.id = id; <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span>()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;User&#123;&quot;</span> + <span class="string">&quot;id=&quot;</span> + id +<span class="string">&quot;, age=&quot;</span> + age +<span class="string">&#x27;&#125;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//线程类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">SubThread</span> <span class="title">extends</span> <span class="title">Thread</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> User user; <span class="comment">//要更新的 User 对象</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//创建 AtomicIntegerFieldUpdater 更新器</span></span><br><span class="line">    <span class="keyword">private</span> AtomicIntegerFieldUpdater&lt;User&gt; updater = AtomicIntegerFieldUpdater.newUpdater(User.<span class="keyword">class</span>, <span class="string">&quot;age&quot;</span>);</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">SubThread</span>(<span class="params">User user</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">this</span>.user = user;</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">        <span class="comment">//在子线程中对 user 对象的 age 字段自增 10 次</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot;的值为&quot;</span> + updater.getAndIncrement(user));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//主类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span> &#123;</span><br><span class="line">        User user = <span class="keyword">new</span> User(<span class="number">1234</span>, <span class="number">10</span>);</span><br><span class="line">        <span class="comment">//开启 10 个线程</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> SubThread(user).start();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.<span class="keyword">out</span>.println( user );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h1 id="线程间的通信"><a href="#线程间的通信" class="headerlink" title="线程间的通信"></a><strong>线程间的通信</strong></h1><h2 id="等待-通知机制"><a href="#等待-通知机制" class="headerlink" title="等待/通知机制"></a><strong>等待/通知机制</strong></h2><h3 id="什么是等待通知机制"><a href="#什么是等待通知机制" class="headerlink" title="什么是等待通知机制"></a><strong>什么是等待通知机制</strong></h3><p>在单线程编程中,要执行的操作需要满足一定的条件才能执行,可以把这个操作放在 if 语句块中。在多线程编程中,可能 A 线程的条件没有满足只是暂时的, 稍后其 他的线程 B 可能会更新条件使得 A 线程的条件得到满足. 可以将 A 线 程暂停,直到它的条件得到满足后再将 A 线程唤醒.它的伪代码：<br><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">atomics&#123; <span class="regexp">//</span>原子操作</span><br><span class="line">    <span class="keyword">while</span>( 条件不成立 )&#123;</span><br><span class="line">        等待</span><br><span class="line">    &#125;</span><br><span class="line">    当前线程被唤醒条件满足后,继续执行下面的操作</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="等待-通知机制的实现"><a href="#等待-通知机制的实现" class="headerlink" title="等待/通知机制的实现"></a><strong>等待/通知机制的实现</strong></h3><p>Object 类中的 wait()方法可以使执行当前代码的线程等待,暂停执 行,直到接到通知或被中断为止.</p>
<p>注意:</p>
<ol>
<li>wait()方法只能在同步代码块中由锁对象调用</li>
<li>调用 wait()方法,当前线程会释放锁<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">synchronized</span>( 锁对象 )&#123;</span><br><span class="line">    <span class="built_in">while</span>( 条件不成立 )&#123;</span><br><span class="line">        <span class="comment">//通过锁对象调用 wait()方法暂停线程,会释放锁对象</span></span><br><span class="line">        锁对象<span class="selector-class">.wait</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//线程的条件满足了继续向下执行</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
Object 类的 notify()可以唤醒线程,该方法也必须在同步代码块中 由 锁 对 象 调 用 . 没 有 使 用 锁 对 象 调 用 wait()/notify() 会 抛 出 IlegalMonitorStateExeption 异常. 如果有多个等待的线程,notify()方法 只能唤醒其中的一个. 在同步代码块中调用 notify()方法后,并不会立 即释放锁对象,需要等当前同步代码块执行完后才会释放锁对象,一般 将 notify()方法放在同步代码块的最后. 它的伪代码如下:<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">synchronized</span>( 锁对象 )&#123;</span><br><span class="line">    <span class="comment">//执行修改保护条件的代码</span></span><br><span class="line">    <span class="comment">//唤醒其他线程</span></span><br><span class="line">    锁对象<span class="selector-class">.notify</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
实现：<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test03</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>) throws InterruptedException</span> &#123;</span><br><span class="line">        String <span class="keyword">lock</span> = <span class="string">&quot;wkcto&quot;</span>; <span class="comment">//定义一个字符串作为锁对象</span></span><br><span class="line">        Thread t1 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            @Override</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">                synchronized (<span class="keyword">lock</span>) &#123;</span><br><span class="line">                    System.<span class="keyword">out</span>.println(<span class="string">&quot;线程 1 开始等待: &quot;</span> + System.currentTimeMillis());</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="keyword">lock</span>.wait(); <span class="comment">//线程等待,会释放锁对象,当前线程转入 blocked 阻塞状态</span></span><br><span class="line">                    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                        e.printStackTrace();</span><br><span class="line">                    &#125;</span><br><span class="line">                    System.<span class="keyword">out</span>.println(<span class="string">&quot;线程 1 结束等待:&quot;</span> + System.currentTimeMillis());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">//定义第二个线程,在第二个线程中唤醒第一个线程</span></span><br><span class="line">        Thread t2 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            @Override</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">                <span class="comment">//notify()方法也需要在同步代码块中,由锁对象调用</span></span><br><span class="line">                synchronized (<span class="keyword">lock</span>)&#123;</span><br><span class="line">                    System.<span class="keyword">out</span>.println(<span class="string">&quot;线程 2 开始唤醒 : &quot;</span> + System.currentTimeMillis());</span><br><span class="line">                    <span class="keyword">lock</span>.notify(); <span class="comment">//唤醒在 lock 锁对象上等待的某一个线程</span></span><br><span class="line">                    System.<span class="keyword">out</span>.println(<span class="string">&quot;线程 2 结束唤醒 : &quot;</span> + System.currentTimeMillis());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        t1.start(); <span class="comment">//开启 t1 线程,t1 线程等待</span></span><br><span class="line">        Thread.sleep(<span class="number">3000</span>); <span class="comment">//main 线程睡眠 3 秒,确保 t1 入睡</span></span><br><span class="line">        t2.start(); <span class="comment">//t1 线程开启 3 秒后,再开启 t2 线程唤醒 t1 线程</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
注意：<strong>notify()方法后不会立即释放锁对象</strong></li>
</ol>
<p><strong>interrupt()方法会中断 wait()</strong></p>
<h3 id="notify-与-notifyAll"><a href="#notify-与-notifyAll" class="headerlink" title="notify()与 notifyAll()"></a><strong>notify()与 notifyAll()</strong></h3><p>notify()一次只能唤醒一个线程,如果有多个等待的线程,只能随机 唤醒其中的某一个; 想要唤醒所有等待线程,需要调用 notifyAll()</p>
<h3 id="wait-long-的使用"><a href="#wait-long-的使用" class="headerlink" title="wait(long)的使用"></a><strong>wait(long)的使用</strong></h3><p>wait(long)带有 long 类型参数的 wait()等待,如果在参数指定的时间 内没有被唤醒,超时后会自动唤醒</p>
<h2 id="ThreadLocal-的使用"><a href="#ThreadLocal-的使用" class="headerlink" title="ThreadLocal 的使用"></a><strong>ThreadLocal 的使用</strong></h2><p>除了控制资源的访问外, 还可以通过增加资源来保证线程安全. ThreadLocal 主要解决为每个线程绑定自己的值.</p>
<h1 id="LOCK显示锁"><a href="#LOCK显示锁" class="headerlink" title="LOCK显示锁"></a><strong>LOCK显示锁</strong></h1><p>在 JDK5 中 增 加 了 Lock 锁 接 口 , 有 ReentrantLock 实 现 类,ReentrantLock 锁称为可重入锁, 它功能比 synchronized 多。</p>
<h2 id="锁的可重入性"><a href="#锁的可重入性" class="headerlink" title="锁的可重入性"></a><strong>锁的可重入性</strong></h2><p>锁的可重入是指,当一个线程获得一个对象锁后,再次请求该对象 锁时是可以获得该对象的锁的<br><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test01</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> synchronized <span class="keyword">void</span> <span class="title">sm1</span>()</span>&#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;同步方法1&quot;</span>);</span><br><span class="line">        <span class="comment">//线程执行 sm1()方法,默认 this 作为锁对象,在 sm1()方法中调用了 sm2()方法,注意 当前线程还是持有 this 锁对象的</span></span><br><span class="line">        <span class="comment">//sm2()同步方法默认的锁对象也是 this 对象, 要执行 sm2()必须先获得 this 锁对象, 当前 this 对象被当前线程持有,可以 再次获得 this 对象,</span></span><br><span class="line">        <span class="comment">// 这就是锁的可重入性. 假设锁不可 重入的话,可能会造成死锁</span></span><br><span class="line">        sm2();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> synchronized <span class="keyword">void</span> <span class="title">sm2</span>()</span> &#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;同步方法 2&quot;</span>);</span><br><span class="line">        sm3();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> synchronized <span class="keyword">void</span> <span class="title">sm3</span>()</span> &#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;同步方法 3&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span> &#123;</span><br><span class="line">        Test01 obj = <span class="keyword">new</span> Test01();</span><br><span class="line">        <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            @Override</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">                obj.sm1();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="ReentrantLock"><a href="#ReentrantLock" class="headerlink" title="ReentrantLock"></a><strong>ReentrantLock</strong></h2><p>ReentrantLock的基本使用:</p>
<p>调用 lock()方法获得锁, 调用 unlock()释放锁<br><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test02</span> &#123;</span><br><span class="line">    <span class="comment">//定义显示锁</span></span><br><span class="line">    <span class="keyword">static</span> Lock <span class="keyword">lock</span> = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line">    <span class="comment">//定义方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sm</span>()</span>&#123;</span><br><span class="line">        <span class="comment">//先获得锁</span></span><br><span class="line">        <span class="keyword">lock</span>.<span class="keyword">lock</span>();</span><br><span class="line">        <span class="comment">//for 循环就是同步代码块</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">            System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot; -- &quot;</span> + i);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//释放锁</span></span><br><span class="line">        <span class="keyword">lock</span>.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span> &#123;</span><br><span class="line">        Runnable r = <span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            @Override</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">                sm();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//启动三个线程</span></span><br><span class="line">        <span class="keyword">new</span> Thread(r).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(r).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(r).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>使用 Lock 锁同步不同方法中的同步代码块<br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test03 &#123;</span><br><span class="line">    static Lock lock = <span class="keyword">new</span> <span class="constructor">ReentrantLock()</span>; <span class="comment">//定义锁对象</span></span><br><span class="line">    public static void sm1<span class="literal">()</span>&#123;</span><br><span class="line">        <span class="comment">//经常在 try 代码块中获得 Lock 锁, 在 finally 子句中释放锁</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;来到了这里&quot;</span>);</span><br><span class="line">            lock.lock<span class="literal">()</span>;</span><br><span class="line">            <span class="comment">//获得锁</span></span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;-- method 1 -- &quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span> );</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="keyword">new</span> <span class="constructor">Random()</span>.next<span class="constructor">Int(1000)</span>);</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;-- method 1 -- &quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span> );</span><br><span class="line">        &#125; catch (InterruptedException e) &#123;</span><br><span class="line">            e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">        &#125; finally &#123;</span><br><span class="line">            lock.unlock<span class="literal">()</span>; <span class="comment">//释放锁</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    public static void sm2<span class="literal">()</span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;来到了这里&quot;</span>);</span><br><span class="line">            lock.lock<span class="literal">()</span>;</span><br><span class="line">            <span class="comment">//获得锁</span></span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;-- method 22 -- &quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span> );</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="keyword">new</span> <span class="constructor">Random()</span>.next<span class="constructor">Int(1000)</span>);</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;-- method 22 -- &quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span> );</span><br><span class="line">        &#125; catch (InterruptedException e) &#123;</span><br><span class="line">            e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">        &#125; finally &#123;</span><br><span class="line">            lock.unlock<span class="literal">()</span>; <span class="comment">//释放锁</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) &#123;</span><br><span class="line">        Runnable r1 = <span class="keyword">new</span> <span class="constructor">Runnable()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span> &#123;</span><br><span class="line">                sm1<span class="literal">()</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        Runnable r2 = <span class="keyword">new</span> <span class="constructor">Runnable()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span> &#123;</span><br><span class="line">                sm2<span class="literal">()</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r1</span>)</span>.start<span class="literal">()</span>;</span><br><span class="line">        <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r1</span>)</span>.start<span class="literal">()</span>;</span><br><span class="line">        <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r1</span>)</span>.start<span class="literal">()</span>;</span><br><span class="line">        <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r2</span>)</span>.start<span class="literal">()</span>;</span><br><span class="line">        <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r2</span>)</span>.start<span class="literal">()</span>;</span><br><span class="line">        <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r2</span>)</span>.start<span class="literal">()</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="ReentrantLock-锁的可重入性"><a href="#ReentrantLock-锁的可重入性" class="headerlink" title="ReentrantLock 锁的可重入性"></a><strong>ReentrantLock 锁的可重入性</strong></h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test04</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">Subthread</span> <span class="title">extends</span> <span class="title">Thread</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> Lock <span class="keyword">lock</span> = <span class="keyword">new</span> ReentrantLock(); <span class="comment">//定义锁对象</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">int</span> num = <span class="number">0</span>;<span class="comment">//定义变量</span></span><br><span class="line">        @Override <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt;<span class="number">10000</span> ; i++) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">//可重入锁指可以反复获得该锁</span></span><br><span class="line">                    <span class="keyword">lock</span>.<span class="keyword">lock</span>();</span><br><span class="line">                    <span class="keyword">lock</span>.<span class="keyword">lock</span>();</span><br><span class="line">                    num++;</span><br><span class="line">                &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">                    <span class="keyword">lock</span>.unlock();</span><br><span class="line">                    <span class="keyword">lock</span>.unlock();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>) throws InterruptedException</span> &#123;</span><br><span class="line">        Subthread t1 = <span class="keyword">new</span> Subthread();</span><br><span class="line">        Subthread t2 = <span class="keyword">new</span> Subthread();</span><br><span class="line">        t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line">        t1.<span class="keyword">join</span>();</span><br><span class="line">        t2.<span class="keyword">join</span>();</span><br><span class="line">        System.<span class="keyword">out</span>.println(Subthread.num);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>lockInterruptibly()方法</p>
<p>lockInterruptibly() 方法的作用:如果当前线程未被中断则获得锁, 如果当前线程被中断则出现异常。lock方法不会被中断。<br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test05 &#123;</span><br><span class="line">    static <span class="keyword">class</span> Servier&#123;</span><br><span class="line">        <span class="keyword">private</span> Lock lock = <span class="keyword">new</span> <span class="constructor">ReentrantLock()</span>; <span class="comment">//定义锁对象</span></span><br><span class="line">        public void service<span class="constructor">Method()</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//lock.lock(); //获得锁定,即使调用了线程的 interrupt()方法,也没有真正的中断线程</span></span><br><span class="line">                lock.lock<span class="constructor">Interruptibly()</span>; <span class="comment">//如果线程被中断了,不会获得锁,会产生异常</span></span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;-- begin lock&quot;</span>);</span><br><span class="line">                <span class="comment">//执行一段耗时的操作</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; Integer.MAX_VALUE; i++) &#123;</span><br><span class="line">                    <span class="keyword">new</span> <span class="constructor">StringBuilder()</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot; -- end lock&quot;</span>);</span><br><span class="line">            &#125; catch (InterruptedException e) &#123;</span><br><span class="line">                e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">            &#125; finally &#123;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot; ***** 释放锁&quot;</span>);</span><br><span class="line">                lock.unlock<span class="literal">()</span>; <span class="comment">//释放锁</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) throws InterruptedException &#123;</span><br><span class="line">        Servier s = <span class="keyword">new</span> <span class="constructor">Servier()</span>;</span><br><span class="line">        Runnable r = <span class="keyword">new</span> <span class="constructor">Runnable()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span> &#123;</span><br><span class="line">                s.service<span class="constructor">Method()</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        Thread t1 = <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r</span>)</span>;</span><br><span class="line">        t1.start<span class="literal">()</span>;</span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">50</span>);</span><br><span class="line">        Thread t2 = <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r</span>)</span>;</span><br><span class="line">        t2.start<span class="literal">()</span>;</span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">50</span>);</span><br><span class="line">        t2.interrupt<span class="literal">()</span>; <span class="comment">//中断 t2 线程</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>对于 synchronized 内部锁来说,如果一个线程在等待锁,只有两个结 果:要么该线程获得锁继续执行;要么就保持等待。对于 ReentrantLock 可重入锁来说,提供另外一种可能,在等待锁的过程中,程序可以根据需要取消对锁的请求。</p>
<p><strong>通过 ReentrantLock 锁的 lockInterruptibly()方法避免死锁的产生</strong><br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test06 &#123;</span><br><span class="line">    static <span class="keyword">class</span> IntLock implements Runnable&#123;</span><br><span class="line">        <span class="comment">//创建两个 ReentrantLock 锁对象</span></span><br><span class="line">        public static ReentrantLock lock1 = <span class="keyword">new</span> <span class="constructor">ReentrantLock()</span>;</span><br><span class="line">        public static ReentrantLock lock2 = <span class="keyword">new</span> <span class="constructor">ReentrantLock()</span>;</span><br><span class="line">        <span class="built_in">int</span> lockNum;</span><br><span class="line">        <span class="comment">//定义整数变量,决定使用哪个锁</span></span><br><span class="line">        public <span class="constructor">IntLock(<span class="params">int</span> <span class="params">lockNum</span>)</span> &#123;</span><br><span class="line">            this.lockNum = lockNum;</span><br><span class="line">        &#125;</span><br><span class="line">        @Override</span><br><span class="line">        public void run<span class="literal">()</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (lockNum % <span class="number">2</span><span class="operator"> == </span><span class="number">1</span>) &#123;</span><br><span class="line">                    <span class="comment">//奇数,先锁 1,再锁 2</span></span><br><span class="line">                    lock1.lock<span class="constructor">Interruptibly()</span>;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;获得锁 1,还需要获得锁 2&quot;</span>);</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="keyword">new</span> <span class="constructor">Random()</span>.next<span class="constructor">Int(500)</span>);</span><br><span class="line">                    lock2.lock<span class="constructor">Interruptibly()</span>;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;同时获得了锁1 与锁 2....&quot;</span>);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123; <span class="comment">//偶数,先锁 2,再锁 1</span></span><br><span class="line">                    lock2.lock<span class="constructor">Interruptibly()</span>;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;获得锁 2,还需要获得锁 1&quot;</span>);</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="keyword">new</span> <span class="constructor">Random()</span>.next<span class="constructor">Int(500)</span>);</span><br><span class="line">                    lock1.lock<span class="constructor">Interruptibly()</span>;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;同时获得了锁 1 与锁 2....&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;catch (InterruptedException e)&#123;</span><br><span class="line">                e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">            &#125;finally &#123;</span><br><span class="line">                <span class="keyword">if</span> ( lock1.is<span class="constructor">HeldByCurrentThread()</span>) <span class="comment">//判断当前线程是否持有该锁</span></span><br><span class="line">                    lock1.unlock<span class="literal">()</span>;</span><br><span class="line">                <span class="keyword">if</span> (lock2.is<span class="constructor">HeldByCurrentThread()</span>)</span><br><span class="line">                    lock2.unlock<span class="literal">()</span>;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;线程退出&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) throws InterruptedException&#123;</span><br><span class="line">        IntLock intLock1 = <span class="keyword">new</span> <span class="constructor">IntLock(11)</span>;</span><br><span class="line">        IntLock intLock2 = <span class="keyword">new</span> <span class="constructor">IntLock(22)</span>;</span><br><span class="line">        Thread t1 = <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">intLock1</span>)</span>;</span><br><span class="line">        Thread t2 = <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">intLock2</span>)</span>;</span><br><span class="line">        t1.start<span class="literal">()</span>;</span><br><span class="line">        t2.start<span class="literal">()</span>;</span><br><span class="line">        <span class="comment">//在 main 线程,等待 3000 秒,如果还有线程没有结束就中断该线程</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">3000</span>);</span><br><span class="line">        <span class="comment">//可以中断任何一个线程来解决死锁, t2 线程会放弃对锁 1 的申请,同时释放锁 2, t1 线程会完成它的任务</span></span><br><span class="line">        <span class="keyword">if</span> (t1.is<span class="constructor">Alive()</span>)&#123;</span><br><span class="line">            t1.interrupt<span class="literal">()</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="tryLock-方法"><a href="#tryLock-方法" class="headerlink" title="tryLock()方法"></a><strong>tryLock()方法</strong></h3><p>tryLock(long time, TimeUnit unit) 的作用在给定等待时长内锁没有被另外的线程持有,并且当前线程也没有被中断,则获得该锁。<strong>通过该方法可以实现锁对象的限时等待</strong><br><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test07</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">TimeLock</span> <span class="title">implements</span> <span class="title">Runnable</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> ReentrantLock <span class="keyword">lock</span> = <span class="keyword">new</span> ReentrantLock(); <span class="comment">//定义锁对象</span></span><br><span class="line">        @Override</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="keyword">lock</span>.tryLock(<span class="number">3</span>, TimeUnit.SECONDS) )&#123; <span class="comment">//获得锁返回 true</span></span><br><span class="line">                    System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot;获得锁,执行耗时任务&quot;</span>);</span><br><span class="line">                    <span class="comment">// Thread.sleep(4000); //假设 Thread-0 线程先持有锁,完成任务需要 4 秒钟,Thread-1 线程尝试获得锁,</span></span><br><span class="line">                    <span class="comment">// Thread-1 线程在 3 秒内还没有获得锁的话,Thread-1 线程会放弃</span></span><br><span class="line">                    <span class="comment">//假设 Thread-0 线程先持有锁,完成任 务需要 2 秒钟,Thread-1 线程尝试获得锁,Thread-1 线程会一直尝试,在它约定尝试的 3 秒内可以获得锁对象</span></span><br><span class="line">                    Thread.sleep(<span class="number">2000</span>);</span><br><span class="line">                &#125;<span class="keyword">else</span> &#123; <span class="comment">//没有获得锁</span></span><br><span class="line">                    System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot;没有获得锁&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="keyword">lock</span>.isHeldByCurrentThread())&#123;</span><br><span class="line">                    <span class="keyword">lock</span>.unlock();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span> &#123;</span><br><span class="line">        TimeLock timeLock = <span class="keyword">new</span> TimeLock();</span><br><span class="line">        Thread t1 = <span class="keyword">new</span> Thread(timeLock);</span><br><span class="line">        Thread t2 = <span class="keyword">new</span> Thread(timeLock);</span><br><span class="line">        t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>tryLock()仅在调用时锁定未被其他线程持有的锁,如果调用方法时,锁对象对其他线程持有,则放弃。调用方法尝试获得没,如果该锁没有被其他线程占用则返回 true 表示锁定成功; 如果锁被其他线程占用 则返回 false,不等待.<br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test08 &#123;</span><br><span class="line">    static <span class="keyword">class</span> Service&#123;</span><br><span class="line">        <span class="keyword">private</span> ReentrantLock lock = <span class="keyword">new</span> <span class="constructor">ReentrantLock()</span>;</span><br><span class="line">        public void service<span class="constructor">Method()</span>&#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (lock.<span class="keyword">try</span><span class="constructor">Lock()</span>)&#123;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;获得锁定&quot;</span>);</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">3000</span>); <span class="comment">//模拟执行任务的时长</span></span><br><span class="line">                &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;没有获得锁定 &quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; catch (InterruptedException e) &#123;</span><br><span class="line">                e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">            &#125; finally &#123;</span><br><span class="line">                <span class="keyword">if</span> (lock.is<span class="constructor">HeldByCurrentThread()</span>)&#123;</span><br><span class="line">                    lock.unlock<span class="literal">()</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) throws InterruptedException &#123;</span><br><span class="line">        Service service = <span class="keyword">new</span> <span class="constructor">Service()</span>;</span><br><span class="line">        Runnable r = <span class="keyword">new</span> <span class="constructor">Runnable()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span> &#123;</span><br><span class="line">                service.service<span class="constructor">Method()</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        Thread t1 = <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r</span>)</span>;</span><br><span class="line">        t1.start<span class="literal">()</span>;</span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">50</span>); <span class="comment">//睡眠 50 毫秒,确保 t1 线程锁定</span></span><br><span class="line">        Thread t2 = <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r</span>)</span>;</span><br><span class="line">        t2.start<span class="literal">()</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="newCondition-方法"><a href="#newCondition-方法" class="headerlink" title="newCondition()方法"></a><strong>newCondition()方法</strong></h2><p>关键字 synchronized 与 wait()/notify()这两个方法一起使用可以实 现等待/通知模式. Lock 锁的 newContition()方法返回 Condition 对 象,Condition 类也可以实现等待/通知模式. 使用 notify()通知时, JVM 会随机唤醒某个等待的线程。</p>
<p>使用 Condition 类可以进行选择性通知. Condition 比较常用的两个方法: await()会使当前线程等待,同时会释放锁,当其他线程调用 signal() 时,线程会重新获得锁并继续执行</p>
<p>signal()用于唤醒一个等待的线程</p>
<p>注意:在调用 Condition 的 await()/signal()方法前,也需要线程持有相 关的 Lock 锁。 调用 await()后线程会释放这个锁,在 singal()调用后会从 当前 Condition 对象的等待队列中,唤醒 一个线程,唤醒 的线程尝试 获得锁, 一旦获得锁成功就继续执行。</p>
<h2 id="公平锁与非公平锁"><a href="#公平锁与非公平锁" class="headerlink" title="公平锁与非公平锁"></a><strong>公平锁与非公平锁</strong></h2><p>大多数情况下,锁的申请都是非公平的. 如果线程1与线程2都在请求 锁 A, 当锁 A 可用时, 系统只是会从阻塞队列中随机的选择一个线程, 不能保证其公平性</p>
<p>公平的锁会按照时间先后顺序,保证先到先得, 公平锁的这一特点不 会出现线程饥饿现象</p>
<p>synchronized 内部锁就是非公平的. ReentrantLock 重入锁提供了一个 构造方法:ReentrantLock(boolean fair) ,当在创建锁对象时实参传递 true 可以把该锁设置为公平锁. 公平锁看起来很公平,但是要实现公 平锁必须要求系统维护一个有序队列,公平锁的实现成本较高,性能也 低. 因此默认情况下锁是非公平的. 不是特别的需求,一般不使用公平锁</p>
<ol>
<li><p>int getHoldCount() 返回当前线程调用 lock()方法的次数</p>
</li>
<li><p>int getQueueLength() 返回正等待获得锁的线程预估数</p>
</li>
<li><p>int getWaitQueueLength(Condition condition) 返回与 Condition 条件 相关的等待的线程预估数</p>
</li>
<li><p>boolean hasQueuedThread(Thread thread) 查询参数指定的线程是否 在等待获得锁</p>
</li>
<li><p>boolean hasQueuedThreads() 查询是否还有线程在等待获得该锁</p>
</li>
<li><p>boolean hasWaiters(Condition condition) 查询是否有线程正在等待指定的 Condition 条件</p>
</li>
<li><p>boolean isFair() 判断是否为公平锁</p>
</li>
<li><p>boolean isHeldByCurrentThread() 判断当前线程是否持有该锁</p>
</li>
<li><p>boolean isLocked() 查询当前锁是否被线程持有；</p>
</li>
</ol>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> ReentrantLock <span class="keyword">lock</span> = <span class="keyword">new</span> ReentrantLock(); <span class="comment">//默认是非公平锁</span></span><br><span class="line"><span class="keyword">static</span> ReentrantLock <span class="keyword">lock</span> = <span class="keyword">new</span> ReentrantLock(<span class="literal">true</span>); <span class="comment">//定义公平锁</span></span><br></pre></td></tr></table></figure>
<h3 id="ReentrantReadWriteLock-读写锁"><a href="#ReentrantReadWriteLock-读写锁" class="headerlink" title="ReentrantReadWriteLock 读写锁"></a><strong>ReentrantReadWriteLock 读写锁</strong></h3><p>synchronized 内部锁与 ReentrantLock 锁都是独占锁(排它锁), 同一 时间只允许一个线程执行同步代码块,可以保证线程的安全性,但是执行效率低。ReentrantReadWriteLock 读写锁是一种改进的排他锁,也可以称作 共享/排他锁。 允许多个线程同时读取共享数据,但是一次只允许一个线程对共享数据进行更新。读写锁通过读锁与写锁来完成读写操作. 线程在读取共享数据前 必须先持有读锁,该读锁可以同时被多个线程持有,即它是共享的.线 程在修改共享数据前必须先持有写锁,写锁是排他的, 一个线程持有 写锁时其他线程无法获得相应的锁。读锁只是在读线程之间共享,任何一个线程持有读锁时,其他线程 都无法获得写锁, 保证线程在读取数据期间没有其他线程对数据进 行更新,使得读线程能够读到数据的最新值,保证在读数据期间共享变 量不被修改</p>
<p>读锁：</p>
<ol>
<li>获得条件：写锁未被任意线程持有</li>
<li>排他性：对读线程是共享的，对写线程是排他的</li>
<li>作用：允许多个读线程可以同时读取共享数据，保证在读共享数据时，没有其他线程对共享数据进行修改</li>
</ol>
<p>写锁：</p>
<ol>
<li>获得条件：该写锁未被其他线程持有，并且相应的读锁也未被其他线程持有</li>
<li>排他性：对读线程或者写线程都是排他的</li>
<li>保证写线程以独占的方式修改共享数据</li>
</ol>
<p>读写锁允许读读共享, 读写互斥,写写互斥</p>
<p>在java.util.concurrent.locks包中定义了ReadWriteLock接口,该接口 中定义了 readLock()返回读锁,定义 writeLock()方法返回写锁. 该接口 的实现类是 ReentrantReadWriteLock</p>
<p>注意 readLock()与 writeLock()方法返回的锁对象是同一个锁的两个 不同的角色, 不是分别获得两个不同的锁. ReadWriteLock 接口实例可 以充当两个角色.读写锁的其他使用方法</p>
<figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">ReadWriteLock rwLock = new ReentrantReadWriteLock()<span class="regexp">//</span>定义读写锁</span><br><span class="line">Lock readLock = rwLock.readLock();<span class="regexp">//</span>获得读锁</span><br><span class="line">Lock writeLock = rwLock.writeLock();<span class="regexp">//</span>获得写锁</span><br><span class="line">readLock.lock(); <span class="regexp">//</span>申请读锁</span><br><span class="line"><span class="regexp">//</span>读数据</span><br><span class="line">try&#123;</span><br><span class="line">    读取共享数据 </span><br><span class="line">&#125;finally&#123;</span><br><span class="line">    readLock.unlock(); <span class="regexp">//</span>总是在 finally 子句中释放锁</span><br><span class="line">&#125;</span><br><span class="line"><span class="regexp">//</span>写数据</span><br><span class="line">writeLock.lock(); <span class="regexp">//</span>申请写锁</span><br><span class="line">try&#123;</span><br><span class="line">    更新修改共享数据 </span><br><span class="line">&#125;finally&#123; </span><br><span class="line">    writeLock.unlock(); <span class="regexp">//</span>总是在 finally 子句中释放锁 </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>读读共享</strong></p>
<p>ReadWriteLock 读写锁可以实现多个线程同时读取共享数据,即读 读共享,可以提高程序的读取数据的效率<br><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test01</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">Service</span> &#123;</span><br><span class="line">        <span class="comment">//定义读写锁</span></span><br><span class="line">        ReadWriteLock readWriteLock = <span class="keyword">new</span> ReentrantReadWriteLock();</span><br><span class="line">        <span class="comment">//定义方法读取数据</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">read</span>()</span>&#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                readWriteLock.readLock().<span class="keyword">lock</span>(); <span class="comment">//获得读锁</span></span><br><span class="line">                System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot;获得读锁,开始读取数据的时间--&quot;</span> + System.currentTimeMillis());</span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">3</span>); <span class="comment">//模拟读取数据用时</span></span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">                readWriteLock.readLock().unlock();<span class="comment">//释放读锁</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span> &#123;</span><br><span class="line">        Service service = <span class="keyword">new</span> Service();</span><br><span class="line">        <span class="comment">//创建 5 个线程,调用 read()方法</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                @Override</span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">                    service.read(); <span class="comment">//在线程中调用 read()读取数据</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;<span class="comment">//运行程序后,这多个 线程几乎可以同时获得锁读,执行 lock()后面的代码</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p><strong>写写互斥</strong></p>
<p>通过 ReadWriteLock 读写锁中的写锁,只允许有一个线程执行 lock() 后面的代码<br><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test02</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">Service</span>&#123;</span><br><span class="line">        <span class="comment">//先定义读写锁</span></span><br><span class="line">        ReadWriteLock readWriteLock = <span class="keyword">new</span> ReentrantReadWriteLock();</span><br><span class="line">        <span class="comment">//定义方法修改数据</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">write</span>()</span>&#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                readWriteLock.writeLock().<span class="keyword">lock</span>(); <span class="comment">//申请获得写锁</span></span><br><span class="line">                System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot;获得写锁,开始修改 数据的时间--&quot;</span> + System.currentTimeMillis());</span><br><span class="line">                Thread.sleep(<span class="number">3000</span>); <span class="comment">//模拟修改数据的用时</span></span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">                System.<span class="keyword">out</span>.println(Thread.currentThread().getName() + <span class="string">&quot;读取数据完毕时的 时间==&quot;</span> + System.currentTimeMillis());</span><br><span class="line">                readWriteLock.writeLock().unlock(); <span class="comment">//释放写锁</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span> &#123;</span><br><span class="line">        Service service = <span class="keyword">new</span> Service();</span><br><span class="line">        <span class="comment">//创建 5 个线程修改数据</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                @Override</span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span> &#123;</span><br><span class="line">                    service.write(); <span class="comment">//调用修改数据的方法</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;<span class="comment">//从执行结果来看,同一时间只有一个线程获得写锁</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p><strong>读写互斥</strong></p>
<p>写锁是独占锁,是排他锁,读线程与写线程也是互斥的</p>
<figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//一个线程获得读锁时,写线程等待; 一个线程获得写锁时,其他线程等待</span></span><br><span class="line">public <span class="keyword">class</span> Test03 &#123;</span><br><span class="line">    static <span class="keyword">class</span> Service &#123;</span><br><span class="line">        <span class="comment">//先定义读写锁</span></span><br><span class="line">        ReadWriteLock readWriteLock = <span class="keyword">new</span> <span class="constructor">ReentrantReadWriteLock()</span>;</span><br><span class="line">        Lock readLock = readWriteLock.read<span class="constructor">Lock()</span>; <span class="comment">//获得读锁</span></span><br><span class="line">        Lock writeLock = readWriteLock.write<span class="constructor">Lock()</span>; <span class="comment">//获得写锁</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 定义方法读取数据</span></span><br><span class="line">        public void read<span class="literal">()</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                readLock.lock<span class="literal">()</span>; <span class="comment">//申请获得读锁</span></span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;获得读锁,开始读取 数据的时间--&quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span>);</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">3000</span>); <span class="comment">//模拟读取数据的用时</span></span><br><span class="line">            &#125; catch (InterruptedException e) &#123;</span><br><span class="line">                e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">            &#125; finally &#123;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;读取数据完毕时的 时间==&quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span>);</span><br><span class="line">                readLock.unlock<span class="literal">()</span>; <span class="comment">//释放读锁</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//定义方法修改数据</span></span><br><span class="line">        public void write<span class="literal">()</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                writeLock.lock<span class="literal">()</span>; <span class="comment">//申请获得写锁</span></span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;获得写锁,开始修改数据的时间--&quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span>);</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">3000</span>); <span class="comment">//模拟修改数据的用时</span></span><br><span class="line">            &#125; catch (InterruptedException e) &#123;</span><br><span class="line">                e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">            &#125; finally &#123;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;修改数据完毕时的 时间==&quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span>);</span><br><span class="line">                writeLock.unlock<span class="literal">()</span>; <span class="comment">//释放写锁</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) &#123;</span><br><span class="line">        Service service = <span class="keyword">new</span> <span class="constructor">Service()</span>;</span><br><span class="line">        <span class="comment">//定义一个线程读数据</span></span><br><span class="line">        <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">new</span> Runnable()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span> &#123;</span><br><span class="line">                service.read<span class="literal">()</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start<span class="literal">()</span>;</span><br><span class="line">        <span class="comment">//定义一个线程写数据</span></span><br><span class="line">        <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">new</span> Runnable()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span> &#123;</span><br><span class="line">                service.write<span class="literal">()</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start<span class="literal">()</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="线程管理"><a href="#线程管理" class="headerlink" title="线程管理"></a><strong>线程管理</strong></h1><h2 id="线程组"><a href="#线程组" class="headerlink" title="线程组"></a><strong>线程组</strong></h2><p>Thread 类有几个构造方法允许在创建线程时指定线程组,如果在创建线程时没有指定线程组则该线程就属于父线程所在的线程组. JVM 在创建 main 线程时会为它指定一个线程组,因此每个 Java 线程都 有一个线程组与之关联, 可以调用线程的 getThreadGroup()方法返回线程组.</p>
<p>线程组开始是出于安全的考虑设计用来区分不同的 Applet,然而 ThreadGroup 并未实现这一目标，<strong>在新开发的系统中,已经不常用线程 组, 现在一般会将一组相关的线程存入一个数组或一个集合中,如果 仅仅是用来区分线程时,可以使用线程名称来区分, 多数情况下,可以忽略线程组。</strong></p>
<figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test01 &#123;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) &#123;</span><br><span class="line">        <span class="comment">// 1) 返回当前 main 线程的线程组</span></span><br><span class="line">        ThreadGroup mainGroup = <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">ThreadGroup()</span>;</span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(mainGroup);</span><br><span class="line">        <span class="comment">//2) 定义线程组,如果不指定所属线程组,则自动归属当前线程所属的线程组中</span></span><br><span class="line">        ThreadGroup group1 = <span class="keyword">new</span> <span class="constructor">ThreadGroup(<span class="string">&quot;group1&quot;</span>)</span>;</span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(group1);</span><br><span class="line">        <span class="comment">//3)定义线程组, 同时指定父线程组</span></span><br><span class="line">        ThreadGroup group2 = <span class="keyword">new</span> <span class="constructor">ThreadGroup(<span class="params">mainGroup</span>, <span class="string">&quot;group2&quot;</span>)</span>;</span><br><span class="line">        <span class="comment">//现在 group1 与 group2 都是 maingroup 线程组中的子线程组, 调用线程组的 getParent()方法返回父线程组</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( group1.get<span class="constructor">Parent()</span><span class="operator"> == </span>mainGroup);<span class="comment">//true</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( group2.get<span class="constructor">Parent()</span><span class="operator"> == </span>mainGroup);<span class="comment">//true</span></span><br><span class="line">        <span class="comment">//4) 在创建线程时指定所属线程组</span></span><br><span class="line">        Runnable r = <span class="keyword">new</span> <span class="constructor">Runnable()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span> &#123;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//在创建线程时,如果没有指定线程组,则默认线程归属到父 线程的线程组中</span></span><br><span class="line">        <span class="comment">//在 main 线程中创建了 t1 线程,称 main 线程为父线程,t1 线 程为子线程, t1 没有指定线程组则 t1 线程就归属到父线程 main 线程 的线程组中</span></span><br><span class="line">        Thread t1 = <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r</span>, <span class="string">&quot;t1&quot;</span>)</span>;</span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println( t1 ); <span class="comment">//Thread[t1,5,main], t1 的线程组 是 main 线程组</span></span><br><span class="line">        <span class="comment">//创建线程时,可以指定线程所属线程组</span></span><br><span class="line">        Thread t2 = <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">group1</span>, <span class="params">r</span>, <span class="string">&quot;t2&quot;</span>)</span>;</span><br><span class="line">        Thread t3 = <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">group2</span>, <span class="params">r</span>, <span class="string">&quot;t3&quot;</span>)</span>;</span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(t2);</span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(t3);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>线程组的基本操作</strong></p>
<ol>
<li>activeCount() 返回当前线程组及子线程组中活动线程的数量(近似值)</li>
<li>activeGroupCount() 返回当前线程组及子线程组中活动线程组的数量 (近似值)</li>
<li>int enumerate(Thread[] list) 将当前线程组中的活动线程复制到参数数组中</li>
<li>enumerate(ThreadGroup[] list) 将当前线程组中的活动线程组复制到 参数数组中</li>
<li>getMaxPriority() 返回线程组的最大优先级,默认是 10</li>
<li>getName() 返回线程组的名称</li>
<li>getParent() 返回父线程组</li>
<li>interrupt() 中断线程组中所有的线程</li>
<li>isDaemon() 判断当前线程组是否为守护线程组</li>
<li>list() 将当前线程组中的活动线程打印出来</li>
<li>parentOf(ThreadGroup g) 判断当前线程组是否为参数线程组的父线程组</li>
<li>setDaemon(boolean daemon) 设置线程组为守护线程组</li>
</ol>
<h2 id="捕获线程的执行异常"><a href="#捕获线程的执行异常" class="headerlink" title="捕获线程的执行异常"></a><strong>捕获线程的执行异常</strong></h2><p>在线程的run方法中,如果有受检异常必须进行捕获处理,如果想要 获 得 run() 方 法 中 出 现 的 运 行 时 异 常 信 息 , 可 以 通 过 回 调 UncaughtExceptionHandler 接口获得哪个线程出现了运行时异常</p>
<p>在 Thread 类中有关处理运行异常的方法有:</p>
<ol>
<li>getDefaultUncaughtExceptionHandler() 获 得 全 局 的 ( 默 认 的)UncaughtExceptionHandler</li>
<li>getUncaughtExceptionHandler() 获 得 当 前 线 程 的 UncaughtExceptionHandler</li>
<li>setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHan dler eh) 设置全局的 UncaughtExceptionHandler</li>
<li>setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 设置当前线程的 UncaughtExceptionHandler</li>
</ol>
<p>当 线 程 运 行 过 程 中 出 现 异 常 ,JVM 会 调 用 Thread 类 的 dispatchUncaughtException(Throwable e) 方 法 , 该 方 法 会 调 用 getUncaughtExceptionHandler().uncaughtException(this, e); 如 果 想 要 获 得 线 程 中 出 现 异 常 的 信 息 , 就 需 要 设 置 线 程 的 UncaughtExceptionHandler<br><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test01</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">void</span> <span class="title function_">main</span>(<span class="params"><span class="built_in">String</span>[] args</span>) &#123;</span><br><span class="line">        <span class="comment">//1)设置线程全局的回调接口</span></span><br><span class="line">        <span class="title class_">Thread</span>.<span class="title function_">setDefaultUncaughtExceptionHandler</span>(<span class="keyword">new</span> <span class="title class_">Thread</span>.<span class="title class_">UncaughtExceptionHandler</span>()&#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">uncaughtException</span>(<span class="params">Thread t, Throwable e</span>) &#123; <span class="comment">//t 参数接收发生异常的线程, e 就是该线程中的异常</span></span><br><span class="line">                <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(t.<span class="title function_">getName</span>() + <span class="string">&quot;线程产生了异常: &quot;</span> + e.<span class="title function_">getMessage</span>());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="title class_">Thread</span> t1 = <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">run</span>(<span class="params"></span>) &#123;</span><br><span class="line">                <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="title class_">Thread</span>.<span class="title function_">currentThread</span>().<span class="title function_">getName</span>() + <span class="string">&quot;开始运行&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="title class_">Thread</span>.<span class="title function_">sleep</span>(<span class="number">2000</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (<span class="title class_">InterruptedException</span> e) &#123;</span><br><span class="line">                    <span class="comment">//线程中的受检异常必须捕获处理</span></span><br><span class="line">                    e.<span class="title function_">printStackTrace</span>();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="number">12</span> / <span class="number">0</span> ); <span class="comment">//会产生算术异常</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        t1.<span class="title function_">start</span>();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span> <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">run</span>(<span class="params"></span>) &#123;</span><br><span class="line">                <span class="title class_">String</span> txt = <span class="literal">null</span>;</span><br><span class="line">                <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(txt.<span class="title function_">length</span>()); <span class="comment">//会产生空指针异常</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).<span class="title function_">start</span>();</span><br><span class="line">        <span class="comment">/* 在实际开发中,这种设计异常处理的方式还是比较常用的,尤其是异常执行的方法</span></span><br><span class="line"><span class="comment">        如果线程产生了异常, JVM 会调用 dispatchUncaughtException()方法,在该方法中调用了 getUncaughtExceptionHandler().uncaughtException(this, e);</span></span><br><span class="line"><span class="comment">        如果当前线程设置了 UncaughtExceptionHandler 回调接口就直接调用它自己的 uncaughtException 方法,</span></span><br><span class="line"><span class="comment">        如果没有设置则调用当前线程所在线程组 UncaughtExceptionHandler 回调接口的 uncaughtException 方法,</span></span><br><span class="line"><span class="comment">        如果线程组也没有设置回调接口,则直接把异常的栈信息定向到 System.err 中</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p><strong>注入 Hook 钩子线程</strong></p>
<p>现在很多软件包括 MySQL, Zookeeper, kafka 等都存在 Hook 线程的 校验机制, 目的是校验进程是否已启动,防止重复启动程序。</p>
<p>Hook 线程也称为钩子线程, 当 JVM 退出的时候会执行 Hook 线程. 经常在程序启动时创建一个.lock 文件, 用.lock 文件校验程序是否启动,在程序退出(JVM 退出)时删除该.lock 文件, 在 Hook 线程中除了防 止重新启动进程外,还可以做资源释放, 尽量避免在 Hook 线程中进行复杂的操作。<br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test &#123;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) &#123;</span><br><span class="line">        <span class="comment">//1)注入 Hook 线程,在程序退出时删除.lock 文件</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">Runtime</span>.</span></span>get<span class="constructor">Runtime()</span>.add<span class="constructor">ShutdownHook(<span class="params">new</span> Thread()</span>&#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span> &#123;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="string">&quot;JVM 退出,会启动当前 Hook 线程,在 Hook 线程中删除.lock 文件&quot;</span>);</span><br><span class="line">                get<span class="constructor">LockFile()</span>.<span class="keyword">to</span><span class="constructor">File()</span>.delete<span class="literal">()</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">//2)程序运行时,检查 lock 文件是否存在,如果 lock 文件存在,则抛出异常</span></span><br><span class="line">        <span class="keyword">if</span> ( get<span class="constructor">LockFile()</span>.<span class="keyword">to</span><span class="constructor">File()</span>.exists<span class="literal">()</span>)&#123;</span><br><span class="line">            throw <span class="keyword">new</span> <span class="constructor">RuntimeException(<span class="string">&quot;程序已启动&quot;</span>)</span>;</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123; <span class="comment">//文件不存在,说明程序是第一次启动,创建 lock 文件</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                get<span class="constructor">LockFile()</span>.<span class="keyword">to</span><span class="constructor">File()</span>.create<span class="constructor">NewFile()</span>;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="string">&quot;程序在启动时创建了 lock 文件&quot;</span>);</span><br><span class="line">            &#125; catch (IOException e) &#123;</span><br><span class="line">                e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//模拟程序运行</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="string">&quot;程序正在运行&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">TimeUnit</span>.</span><span class="module"><span class="identifier">SECONDS</span>.</span></span>sleep(<span class="number">1</span>);</span><br><span class="line">            &#125; catch (InterruptedException e) &#123;</span><br><span class="line">                e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">private</span> static Path get<span class="constructor">LockFile()</span>&#123;</span><br><span class="line">        return <span class="module-access"><span class="module"><span class="identifier">Paths</span>.</span></span>get(<span class="string">&quot;&quot;</span>, <span class="string">&quot;tmp.lock&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h1 id="线程池"><a href="#线程池" class="headerlink" title="线程池"></a><strong>线程池</strong></h1><h2 id="什么是线程池"><a href="#什么是线程池" class="headerlink" title="什么是线程池"></a><strong>什么是线程池</strong></h2><p>可以以 new Thread( () -&gt; { 线程执行的任务 }).start(); 这种形式开启一个线程. 当 run()方法运行结束,线程对象会被 GC 释放。</p>
<p>在真实的生产环境中,可能需要很多线程来支撑整个应用,当线程数量非常多时 ,反而会耗尽 CPU 资源. 如果不对线程进行控制与管理, 反而会影响程序的性能。</p>
<p>线程开销主要包括: 创建与启动线程的开销; 线程销毁开销; 线程调度的开销; 线程数量受限 CPU 处理器数量</p>
<p>线程池就是有效使用线程的一种常用方式。线程池内部可以预先创建一定数量的工作线程,客户端代码直接将任务作为一个对象提交给线程池, 线程池将这些任务缓存在工作队列中, 线程池中的工作线程不断地从队列中取出任务并执行。</p>
<img src="/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/%E7%BA%BF%E7%A8%8B%E6%B1%A0.png" class title="线程池">
<h2 id="JDK-对线程池的支持"><a href="#JDK-对线程池的支持" class="headerlink" title="JDK 对线程池的支持"></a><strong>JDK 对线程池的支持</strong></h2><p>JDK 提供了一套 Executor 框架,可以帮助开发人员有效的使用线程池</p>
<img src="/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/Excutor%E6%A1%86%E6%9E%B6.png" class title="Excutor框架">
<p>线程池的基本使用:<br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test01 &#123;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) &#123;</span><br><span class="line">        <span class="comment">//创建有 5 个线程大小的线程池</span></span><br><span class="line">        ExecutorService fixedThreadPool = <span class="module-access"><span class="module"><span class="identifier">Executors</span>.</span></span><span class="keyword">new</span><span class="constructor">FixedThreadPool(5)</span>;</span><br><span class="line">        <span class="comment">//向线程池中提交 18 个任务,这 18 个任务存储到线程池的阻塞队列中, 线程池中这 5 个线程就从阻塞队列中取任务执行</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">18</span>; i++)&#123;</span><br><span class="line">            fixedThreadPool.execute(<span class="keyword">new</span> <span class="constructor">Runnable()</span> &#123;</span><br><span class="line">                @Override</span><br><span class="line">                public void run<span class="literal">()</span> &#123;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Id()</span> + <span class="string">&quot; 编号的任务在执行任务,开始时间: &quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span>);</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">3000</span>); <span class="comment">//模拟任务执行时长</span></span><br><span class="line">                    &#125; catch (InterruptedException e) &#123;</span><br><span class="line">                        e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>线程池的计划任务:能够在周期时间或者规定延迟后循环执行某项任务<br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test02 &#123;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) &#123;</span><br><span class="line">        <span class="comment">//创建一个有调度功能的线程池</span></span><br><span class="line">        ScheduledExecutorService scheduledExecutorService = <span class="module-access"><span class="module"><span class="identifier">Executors</span>.</span></span><span class="keyword">new</span><span class="constructor">ScheduledThreadPool(10)</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//在延迟 2 秒后执行任务, schedule( Runnable 任务, 延迟时长, 时间单位)</span></span><br><span class="line">        scheduledExecutorService.schedule(<span class="keyword">new</span> <span class="constructor">Runnable()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span> &#123;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Id()</span> + <span class="string">&quot; -- &quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="number">4</span>, TimeUnit.SECONDS);</span><br><span class="line">        <span class="comment">//以固定的频率执行任务,开启任务的时间是固定的, 在 3 秒后执行任务,以后每隔 5 秒重新执行一次</span></span><br><span class="line"></span><br><span class="line">        scheduledExecutorService.schedule<span class="constructor">AtFixedRate(<span class="params">new</span> Runnable()</span> &#123;</span><br><span class="line">            @Override public void run<span class="literal">()</span> &#123;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Id()</span> + <span class="string">&quot;----在固定频率开启任务---&quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">TimeUnit</span>.</span><span class="module"><span class="identifier">SECONDS</span>.</span></span>sleep(<span class="number">3</span>); <span class="comment">//睡眠模拟任务执行时间 ,如果任务执 行时长超过了时间间隔,则任务完成后立即开启下个任务</span></span><br><span class="line">                &#125; catch (InterruptedException e) &#123;</span><br><span class="line">                    e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            &#125;, <span class="number">3</span>, <span class="number">2</span>, TimeUnit.SECONDS);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//在上次任务结束后,在固定延迟后再次执行该任务,不管执行任务耗时多长,总是在任务结束 后的 2 秒再次开启新的任务</span></span><br><span class="line"><span class="comment">//        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() &#123;</span></span><br><span class="line"><span class="comment">//            @Override</span></span><br><span class="line"><span class="comment">//            public void run() &#123;</span></span><br><span class="line"><span class="comment">//                System.out.println(Thread.currentThread().getId() + &quot;----在固定频率开启任务---&quot; + System.currentTimeMillis());</span></span><br><span class="line"><span class="comment">//                try &#123;</span></span><br><span class="line"><span class="comment">//                    TimeUnit.SECONDS.sleep(3); //睡眠模拟任务执行时间 ,如果任务执 行时长超过了时间间隔,则任务完成后立即开启下个任务</span></span><br><span class="line"><span class="comment">//                &#125; catch (InterruptedException e) &#123;</span></span><br><span class="line"><span class="comment">//                    e.printStackTrace();</span></span><br><span class="line"><span class="comment">//                &#125;</span></span><br><span class="line"><span class="comment">//            &#125;</span></span><br><span class="line"><span class="comment">//        &#125;, 3, 2, TimeUnit.SECONDS);</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="核心线程池的底层实现"><a href="#核心线程池的底层实现" class="headerlink" title="核心线程池的底层实现"></a><strong>核心线程池的底层实现</strong></h3><p>查 看 Executors 工 具 类 中 newCachedThreadPool(), newSingleThreadExcecutor(), newFixedThreadPool()源码</p>
<figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="keyword">new</span><span class="type">CachedThreadPool</span>() &#123; </span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="type">ThreadPoolExecutor</span>(<span class="number">0</span>, Integer.MAX_VALUE, <span class="number">60</span>L,</span><br><span class="line">    TimeUnit.SECONDS, <span class="keyword">new</span> <span class="type">SynchronousQueue</span>&lt;Runnable&gt;()); </span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//该线程池在极端情况下,每次提交新的任务都会创建新的线程执行. 适合用来执行大量 耗时短并且提交频繁的任务</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="keyword">new</span><span class="type">FixedThreadPool</span>(int nThreads) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="type">ThreadPoolExecutor</span>(nThreads, nThreads, <span class="number">0</span>L, </span><br><span class="line">            TimeUnit.MILLISECONDS, <span class="keyword">new</span> <span class="type">LinkedBlockingQueue</span>&lt;Runnable&gt;());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="keyword">new</span><span class="type">SingleThreadExecutor</span>() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="type">FinalizableDelegatedExecutorService</span> (<span class="keyword">new</span> <span class="type">ThreadPoolExecutor</span>(<span class="number">1</span>, <span class="number">1</span>,<span class="number">0</span>L,</span><br><span class="line">            TimeUnit.MILLISECONDS, <span class="keyword">new</span> <span class="type">LinkedBlockingQueue</span>&lt;Runnable&gt;()));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Excutors 工 具 类 中 返 回 线 程 池 的 方 法 底 层 都 使 用 了 ThreadPoolExecutor 线程池,这些方法都是 ThreadPoolExecutor 线程池的封装。</p>
<figure class="highlight aspectj"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ThreadPoolExecutor 的构造方法: </span></span><br><span class="line"><span class="keyword">public</span> ThreadPoolExecutor(<span class="keyword">int</span> corePoolSize,</span><br><span class="line">                            <span class="keyword">int</span> maximumPoolSize,</span><br><span class="line">                            <span class="keyword">long</span> keepAliveTime,</span><br><span class="line">                            TimeUnit unit,</span><br><span class="line">                            BlockingQueue&lt;Runnable&gt; workQueue,</span><br><span class="line">                            ThreadFactory threadFactory,</span><br><span class="line">                            RejectedExecutionHandler <span class="keyword">handler</span>)</span><br></pre></td></tr></table></figure>
<p>各个参数的含义：<br>参考前面面试题笔记。</p>
<p>Executors 工具类提供的静态方法返回的线程池默认的拒绝策略是 AbortPolicy 抛出异常,如果内置的拒绝策略无法满足实际需求,可以扩展 RejectedExecutionHandler 接口<br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test03 &#123;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) &#123;</span><br><span class="line">        <span class="comment">//定义任务</span></span><br><span class="line">        Runnable r = <span class="keyword">new</span> <span class="constructor">Runnable()</span> &#123;</span><br><span class="line">            @Override public void run<span class="literal">()</span> &#123;</span><br><span class="line">                <span class="built_in">int</span> num = <span class="keyword">new</span> <span class="constructor">Random()</span>.next<span class="constructor">Int(5)</span>;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Id()</span> + <span class="string">&quot;--&quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span> + <span class="string">&quot;开始睡眠&quot;</span> + num + <span class="string">&quot;秒&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">TimeUnit</span>.</span><span class="module"><span class="identifier">SECONDS</span>.</span></span>sleep(num);</span><br><span class="line">                &#125; catch (InterruptedException e) &#123;</span><br><span class="line">                    e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//创建线程池, 自定义拒绝策略</span></span><br><span class="line">        ThreadPoolExecutor threadPoolExecutor = <span class="keyword">new</span> <span class="constructor">ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, <span class="params">new</span> LinkedBlockingQueue&lt;&gt;(10)</span>, <span class="module-access"><span class="module"><span class="identifier">Executors</span>.</span></span>default<span class="constructor">ThreadFactory()</span>, <span class="keyword">new</span> <span class="constructor">RejectedExecutionHandler()</span>&#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void rejected<span class="constructor">Execution(Runnable <span class="params">r</span>, ThreadPoolExecutor <span class="params">executor</span>)</span> &#123;</span><br><span class="line">                <span class="comment">//r 就是请求的任务, executor 就是当前线程池</span></span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(r + <span class="string">&quot; is discarding..&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">//向线程池提交若干任务</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; Integer.MAX_VALUE; i++) &#123;</span><br><span class="line">            threadPoolExecutor.submit(r);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="ThreadFactory"><a href="#ThreadFactory" class="headerlink" title="ThreadFactory"></a><strong>ThreadFactory</strong></h3><p>线程池中的线程从哪儿来的? 答案就是 ThreadFactory</p>
<p>ThreadFactory 是一个接口,只有一个用来创建线程的方法</p>
<p><code>Thread newThread(Runnable r);</code></p>
<p>当线程池中需要创建线程时就会调用该方法</p>
<p>自定义线程工厂<br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test04 &#123;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) throws InterruptedException&#123;</span><br><span class="line">        <span class="comment">//定义任务</span></span><br><span class="line">        Runnable r = <span class="keyword">new</span> <span class="constructor">Runnable()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span> &#123;</span><br><span class="line">                <span class="built_in">int</span> num = <span class="keyword">new</span> <span class="constructor">Random()</span>.next<span class="constructor">Int(10)</span>;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Id()</span> + <span class="string">&quot;--&quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span> + <span class="string">&quot;开始睡眠:&quot;</span> + num + <span class="string">&quot;秒&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">TimeUnit</span>.</span><span class="module"><span class="identifier">SECONDS</span>.</span></span>sleep(num);</span><br><span class="line">                &#125;catch (InterruptedException e)&#123;</span><br><span class="line">                    e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//创建线程池, 使用自定义线程工厂, 采用默认的拒绝策略是抛出异常</span></span><br><span class="line">        ExecutorService executorService = <span class="keyword">new</span> <span class="constructor">ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, <span class="params">new</span> SynchronousQueue&lt;&gt;()</span>, <span class="keyword">new</span> <span class="constructor">ThreadFactory()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public Thread <span class="keyword">new</span><span class="constructor">Thread(Runnable <span class="params">r</span>)</span> &#123;</span><br><span class="line">                <span class="comment">//根据参数 r 接收的任务,创建一个线程</span></span><br><span class="line">                Thread t = <span class="keyword">new</span> <span class="constructor">Thread(<span class="params">r</span>)</span>;</span><br><span class="line">                t.set<span class="constructor">Daemon(<span class="params">true</span>)</span>; <span class="comment">//设置为守护线程, 当主线程运行结束,线程池中的 线程会自动退出</span></span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="string">&quot;创建了线程: &quot;</span> + t);</span><br><span class="line">                return t ;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">//提交 5 个任务, 当给当前线程池提交的任务超过 5 个时,线程池默认抛出异常</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            executorService.submit(r);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//主线程睡眠</span></span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">10000</span>);</span><br><span class="line">        <span class="comment">//主线程睡眠超时, 主线程结束, 线程池中的线程会自动退出</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="监控线程池"><a href="#监控线程池" class="headerlink" title="监控线程池"></a><strong>监控线程池</strong></h3><p>ThreadPoolExecutor 提供了一组方法用于监控线程池</p>
<ol>
<li><p>int getActiveCount() 获得线程池中当前活动线程的数量</p>
</li>
<li><p>long getCompletedTaskCount() 返回线程池完成任务的数量</p>
</li>
<li><p>int getCorePoolSize() 线程池中核心线程的数量</p>
</li>
<li><p>int getLargestPoolSize() 返回线程池曾经达到的线程的最大数</p>
</li>
<li><p>int getMaximumPoolSize() 返回线程池的最大容量</p>
</li>
<li><p>int getPoolSize() 当前线程池的大小</p>
</li>
<li><p>BlockingQueue<Runnable> getQueue() 返回阻塞队列</Runnable></p>
</li>
<li><p>long getTaskCount() 返回线程池收到的任务总数</p>
<figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">class</span> Test05 &#123;</span><br><span class="line">    public static void main(String<span class="literal">[]</span> args) throws InterruptedException&#123;</span><br><span class="line">        <span class="comment">//先定义任务</span></span><br><span class="line">        Runnable r = <span class="keyword">new</span> <span class="constructor">Runnable()</span> &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public void run<span class="literal">()</span>&#123;</span><br><span class="line">                <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>current<span class="constructor">Thread()</span>.get<span class="constructor">Name()</span> + <span class="string">&quot;编号的线程开始执行：&quot;</span> + <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>current<span class="constructor">TimeMillis()</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">10000</span>);<span class="comment">//睡眠20秒，模拟任务执行时长</span></span><br><span class="line">                &#125;catch (InterruptedException e)&#123;</span><br><span class="line">                    e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//定义线程池</span></span><br><span class="line">        ThreadPoolExecutor poolExecutor = <span class="keyword">new</span> <span class="constructor">ThreadPoolExecutor(2, 5, 0, TimeUnit.SECONDS, <span class="params">new</span> ArrayBlockingQueue&lt;&gt;(5)</span>, <span class="module-access"><span class="module"><span class="identifier">Executors</span>.</span></span>default<span class="constructor">ThreadFactory()</span>, <span class="keyword">new</span> ThreadPoolExecutor.<span class="constructor">DiscardPolicy()</span>);</span><br><span class="line">        <span class="comment">//向线程池提交 30 个任务</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; <span class="number">30</span>; i++) &#123;</span><br><span class="line">            poolExecutor.submit(r);</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="string">&quot; 当 前 线 程 池 核 心 线 程 数 量 : &quot;</span> + poolExecutor.get<span class="constructor">CorePoolSize()</span> + <span class="string">&quot;, 最大线程数:&quot;</span> + poolExecutor.get<span class="constructor">MaximumPoolSize()</span> + <span class="string">&quot;,当 前 线 程 池 大 小 :&quot;</span> + poolExecutor.get<span class="constructor">PoolSize()</span> + <span class="string">&quot;, 活 动 线 程 数 量 :&quot;</span> + poolExecutor.get<span class="constructor">ActiveCount()</span>+ <span class="string">&quot;,收到任务数量:&quot;</span> + poolExecutor.get<span class="constructor">TaskCount()</span> + <span class="string">&quot;,完成任务 数 : &quot;</span> + poolExecutor.get<span class="constructor">CompletedTaskCount()</span> + <span class="string">&quot;, 等 待 任 务 数 :&quot;</span> + poolExecutor.get<span class="constructor">Queue()</span>.size<span class="literal">()</span>) ;</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">TimeUnit</span>.</span><span class="module"><span class="identifier">MILLISECONDS</span>.</span></span>sleep(<span class="number">500</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="string">&quot;-----------------------------------------------&quot;</span>);</span><br><span class="line">        <span class="keyword">while</span> ( poolExecutor.get<span class="constructor">ActiveCount()</span> &gt;= <span class="number">0</span> )&#123;</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">System</span>.</span></span>out.println(<span class="string">&quot; 当 前 线 程 池 核 心 线 程 数 量 : &quot;</span> + poolExecutor.get<span class="constructor">CorePoolSize()</span> + <span class="string">&quot;, 最大线程数:&quot;</span> + poolExecutor.get<span class="constructor">MaximumPoolSize()</span> + <span class="string">&quot;,当前 线 程 池 大 小 :&quot;</span> + poolExecutor.get<span class="constructor">PoolSize()</span> + <span class="string">&quot;, 活 动 线 程 数 量 :&quot;</span> + poolExecutor.get<span class="constructor">ActiveCount()</span>+ <span class="string">&quot;,收到任务数量:&quot;</span> + poolExecutor.get<span class="constructor">TaskCount()</span> + <span class="string">&quot;,完成任务 数 : &quot;</span> + poolExecutor.get<span class="constructor">CompletedTaskCount()</span> + <span class="string">&quot;, 等 待 任 务 数 :&quot;</span> + poolExecutor.get<span class="constructor">Queue()</span>.size<span class="literal">()</span>);</span><br><span class="line">            <span class="module-access"><span class="module"><span class="identifier">Thread</span>.</span></span>sleep(<span class="number">1000</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="扩展线程池"><a href="#扩展线程池" class="headerlink" title="扩展线程池"></a><strong>扩展线程池</strong></h3></li>
</ol>
<p>有时需要对线程池进行扩展,如在监控每个任务的开始和结束时 间,或者自定义一些其他增强的功能。</p>
<p>ThreadPoolExecutor 线程池提供了两个方法:</p>
<ol>
<li><p>protected void afterExecute(Runnable r, Throwable t)</p>
</li>
<li><p>protected void beforeExecute(Thread t, Runnable r)</p>
</li>
</ol>
<p>在线程池执行某个任务前会调用 beforeExecute()方法,在任务结束 后(任务异常退出)会执行 afterExecute()方法</p>
<p>查看 ThreadPoolExecutor 源码,在该类中定义了一个内部类 Worker, ThreadPoolExecutor 线程池中的工作线程就是 Worker 类的实例, Worker 实例在执行时会调用 beforeExecute()与 afterExecute()方法</p>
<hr>
<p>暂时性小总结：</p>
<p>不管是Executors.newFixedThreadPool(5)方法还是Executors.newScheduledThreadPool(10)方法，创建出来的其实都是ThreadPoolExcutor类，这个<code>ThreadPoolExcutor</code>类就是一个线程池。而线程池的主要任务就是创建线程，接收任务。</p>
<p>先说创建线程，一般我们自己创建线程都是继承一个Thread类或者实现Runnable接口，然后重写里面的run()方法。但是线程池它是自己来创建线程的。那么它利用什么创建线程呢？答案是<code>ThreadFactory</code>。</p>
<p>ThreadFactory是一个接口，在Excutors类的内部类DefaultThreadFactory中实现了ThreadFactory接口。</p>
<p>ThreadFactory接口定义了一个newThread(Runnable r)方法，用来创建线程。</p>
<p>当我们创建一个线程池的时候，我们可以在其构造方法中传入ThreadFactory实现类，或者自己实现ThreadFactory接口中的newThread方法。</p>
<p>再来说一下接受任务，接受任务有两个方法execute()和submit()</p>
<ol>
<li><p>execute只能提交Runnable类型的任务，没有返回值，而submit既能提交Runnable类型任务也能提交Callable类型任务，返回Future类型。</p>
</li>
<li><p>execute方法提交的任务异常是直接抛出的，而submit方法是是捕获了异常的，当调用FutureTask的get方法时，才会抛出异常。</p>
</li>
</ol>
<hr>
<h3 id="优化线程池大小"><a href="#优化线程池大小" class="headerlink" title="优化线程池大小"></a><strong>优化线程池大小</strong></h3><p>线程池大小对系统性能是有一定影响的,过大或者过小都会无法 发挥最优的系统性能, 线程池大小不需要非常精确,只要避免极大或 者极小的情况即可, 一般来说,线程池大小需要考虑 CPU 数量,内存大 小等因素。<br>在<Java concurrency in practice>书中给出一个估算线程池大小的公式：</Java></p>
<p>线程池大小 = CPU数量 × 目标CPU的使用率 × (1 + 等待时间与计算时间的比)</p>
<h3 id="线程池死锁"><a href="#线程池死锁" class="headerlink" title="线程池死锁"></a><strong>线程池死锁</strong></h3><p>如果在线程池中执行的 任务 A 在执行过程中又向线程池提交了 任务 B, 任务 B 添加到了线程池的等待队列中，如果任务 A 的结束需 要等待任务 B 的执行结果。 就有可能会出现这种情况： 线程池中所有 的工作线程都处于等待任务处理结果,而这些任务在阻塞队列中等待执行， 线程池中没有可以对阻塞队列中的任务进行处理的线程，这种 等待会一直持续下去，从而造成死锁。</p>
<p>适合给线程池提交相互独立的任务，而不是彼此依赖的任务。 对于彼此依赖的任务，可以考虑分别提交给不同的线程池来执行。</p>
<h3 id="线程池中的异常处理"><a href="#线程池中的异常处理" class="headerlink" title="线程池中的异常处理"></a><strong>线程池中的异常处理</strong></h3><p>在使用 ThreadPoolExecutor 进行 submit 提交任务时，有的任务抛出了异常，但是线程池并没有进行提示，即线程池把任务中的异常给吃掉了，可以把 submit 提交改为 execute 执行，也可以对 ThreadPoolExecutor 线程池进行扩展。对提交的任务进行包装。</p>
<p>自定义线程池类,对 ThreadPoolExecutor 进行扩展(封装)：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test08</span> &#123;</span><br><span class="line">    <span class="comment">//自定义线程池类</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">TraceThreadPollExecutor</span> <span class="keyword">extends</span> <span class="title class_">ThreadPoolExecutor</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">TraceThreadPollExecutor</span><span class="params">(<span class="type">int</span> corePoolSize, <span class="type">int</span> maximumPoolSize,</span></span><br><span class="line"><span class="params">                                       <span class="type">long</span> keepAliveTime, TimeUnit unit, BlockingQueue&lt;Runnable&gt; workQueue)</span> &#123;</span><br><span class="line">            <span class="built_in">super</span>(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//定义方法,对执行的任务进行包装,接收两个参数,第一个参数接收要执行的任务, 第二个参数是一个 Exception 异常</span></span><br><span class="line">        <span class="keyword">public</span> Runnable <span class="title function_">wrap</span><span class="params">(Runnable task, Exception exception)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        task.run();</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                        exception.printStackTrace();</span><br><span class="line">                        <span class="keyword">throw</span> e;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//重写 submit 方法</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Future&lt;?&gt; submit(Runnable task) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">super</span>.submit(wrap(task, <span class="keyword">new</span> <span class="title class_">Exception</span>(<span class="string">&quot;客户跟踪异常&quot;</span>)));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(Runnable command)</span> &#123;</span><br><span class="line">            <span class="built_in">super</span>.execute(wrap(command, <span class="keyword">new</span> <span class="title class_">Exception</span>(<span class="string">&quot;客户跟踪异常&quot;</span>)));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//定义类实现 Runnable 接口,用于计算两个数相除</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">DivideTask</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="type">int</span> x;</span><br><span class="line">        <span class="keyword">private</span> <span class="type">int</span> y;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">DivideTask</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.x = x;</span><br><span class="line">            <span class="built_in">this</span>.y = y;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;计算:&quot;</span> + x + <span class="string">&quot; / &quot;</span> + y + <span class="string">&quot; = &quot;</span> + (x/y));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//创建线程池</span></span><br><span class="line">        <span class="comment">// ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0, TimeUnit.SECONDS, new SynchronousQueue&lt;&gt;());</span></span><br><span class="line">        <span class="comment">//使用自定义的线程池</span></span><br><span class="line">        <span class="type">ThreadPoolExecutor</span> <span class="variable">poolExecutor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TraceThreadPollExecutor</span>(<span class="number">0</span>, Integer.MAX_VALUE, <span class="number">0</span>, TimeUnit.SECONDS, <span class="keyword">new</span> <span class="title class_">SynchronousQueue</span>&lt;&gt;());</span><br><span class="line">        <span class="comment">//向线程池中添加计算两个数相除的任务</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            poolExecutor.submit(<span class="keyword">new</span> <span class="title class_">DivideTask</span>(<span class="number">10</span>, i));</span><br><span class="line">            <span class="comment">// poolExecutor.execute(new DivideTask(10, i));</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="ForkJoinPool-线程池"><a href="#ForkJoinPool-线程池" class="headerlink" title="ForkJoinPool 线程池"></a><strong>ForkJoinPool 线程池</strong></h3><p>“分而治之”是一个有效的处理大数据的方法，著名的 MapReduce 就是采用这种分而治之的思路。 简单点说，如果要处理的 1000 个数据， 但是我们不具备处理1000个数据的能力，可以只处理10个数据， 可以把这 1000 个数据分阶段处理 100 次，每次处理 10 个，把 100 次的处理结果进行合成，形成最后这 1000 个数据的处理结果。</p>
<p>把一个大任务调用 fork()方法分解为若干小的任务,把小任务的处理结果进行 join()合并为大任务的结果。</p>
<p>系统对 ForkJoinPool 线程池进行了优化，提交的任务数量与线程的数量不一定是一对一关系.在多数情况下，一个物理线程实际上需要处理多个逻辑任务。</p>
<img src="/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/ForkJoinPool.png" class title="ForkJoinPool">
<p>ForkJoinPool 线程池中最常用的方法是: <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)向线程池提交一个 ForkJoinTask 任务。ForkJoinTask 任务支持 fork()分解与 join()等待的任务。ForkJoinTask 有两个重要的子类 :RecursiveAction 和 RecursiveTask ,它们的区别在于 RecursiveAction 任务没有返回值, RecursiveTask 任务可以带有返回值。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test09</span> &#123;</span><br><span class="line">    <span class="comment">//计算数列的和, 需要返回结果,可以定义任务继承 RecursiveTask</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">CountTask</span> <span class="keyword">extends</span> <span class="title class_">RecursiveTask</span>&lt;Long&gt; &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">THRESHOLD</span> <span class="operator">=</span> <span class="number">10000</span>; <span class="comment">//定义数据规模的阈值,允许计算 10000 个数内的和,超过该阈值的数列就需要分解</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">TASKNUM</span> <span class="operator">=</span> <span class="number">100</span>; <span class="comment">//定义每次把大任务分解为 100 个小任务</span></span><br><span class="line">        <span class="keyword">private</span> <span class="type">long</span> start; <span class="comment">//计算数列的起始值</span></span><br><span class="line">        <span class="keyword">private</span> <span class="type">long</span> end; <span class="comment">//计算数列的结束值</span></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">CountTask</span><span class="params">(<span class="type">long</span> start, <span class="type">long</span> end)</span> &#123; <span class="built_in">this</span>.start = start; <span class="built_in">this</span>.end = end; &#125;</span><br><span class="line">        <span class="comment">//重写 RecursiveTask 类的 compute()方法,计算数列的结果</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">protected</span> Long <span class="title function_">compute</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>; <span class="comment">//保存计算的结果</span></span><br><span class="line">            <span class="comment">// 判断任务是否需要继续分解,如果当前数列 end 与 start 范围的数超过阈值 THRESHOLD,就需要继续分解</span></span><br><span class="line">            <span class="keyword">if</span> (end - start &lt; THRESHOLD) &#123; <span class="comment">//小于阈值可以直接计算</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="type">long</span> <span class="variable">i</span> <span class="operator">=</span> start; i &lt;= end; i++) &#123;</span><br><span class="line">                    sum += i;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123; <span class="comment">//数列范围超过阈值,需要继续分解</span></span><br><span class="line">                <span class="comment">// 约定每次分解成 100 个小任务,计算每个任务的计算量</span></span><br><span class="line">                <span class="type">long</span> <span class="variable">step</span> <span class="operator">=</span> (start + end) / TASKNUM; <span class="comment">//start = 0 , end = 200000, step = 2000, 如果计算[0,200000]范围内数列的 和, 把该范围的数列分解为 100 个小任务,每个任务计算 2000 个数即可</span></span><br><span class="line">                <span class="comment">// 注意,如果任务划分的层次很深,即 THRESHOLD 阈值太小,每个任务的计 算量很小,层次划分就会很深,可能出现两种情况:</span></span><br><span class="line">                <span class="comment">// 一是系统内的线程数量会越积越多,导致性 能下降严重;</span></span><br><span class="line">                <span class="comment">// 二是分解次数过多,方法调用过多可能会导致栈溢出</span></span><br><span class="line">                <span class="comment">// 创建一个存储任务的集合</span></span><br><span class="line">                ArrayList&lt;CountTask&gt; subTaskList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">                <span class="type">long</span> <span class="variable">pos</span> <span class="operator">=</span> start; <span class="comment">//每个任务的起始位置</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; TASKNUM; i++) &#123;</span><br><span class="line">                    <span class="type">long</span> <span class="variable">lastOne</span> <span class="operator">=</span> pos + step; <span class="comment">//每个任务的结束位置</span></span><br><span class="line">                    <span class="comment">// 调整最后一个任务的结束位置</span></span><br><span class="line">                    <span class="keyword">if</span> (lastOne &gt; end) &#123;</span><br><span class="line">                        lastOne = end;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="comment">//创建子任务</span></span><br><span class="line">                    <span class="type">CountTask</span> <span class="variable">task</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountTask</span>(pos, lastOne);</span><br><span class="line">                    <span class="comment">//把任务添加到集合中</span></span><br><span class="line">                    subTaskList.add(task);</span><br><span class="line">                    <span class="comment">//调用 for()提交子任务</span></span><br><span class="line">                    task.fork();</span><br><span class="line">                    <span class="comment">//调整下个任务的起始位置</span></span><br><span class="line">                    pos += step + <span class="number">1</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">//等待所有的子任务结束后,合并计算结果</span></span><br><span class="line">                <span class="keyword">for</span> (CountTask task : subTaskList) &#123;</span><br><span class="line">                    sum += task.join(); <span class="comment">//join()会一直等待子任务执行完毕返回 执行结果</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> sum;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//创建 ForkJoinPool 线程池</span></span><br><span class="line">        <span class="type">ForkJoinPool</span> <span class="variable">forkJoinPool</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ForkJoinPool</span>();</span><br><span class="line">        <span class="comment">//创建一个大的任务</span></span><br><span class="line">        <span class="type">CountTask</span> <span class="variable">task</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountTask</span>(<span class="number">0L</span>, <span class="number">200000L</span>);</span><br><span class="line">        <span class="comment">//把大任务提交给线程池</span></span><br><span class="line">        ForkJoinTask&lt;Long&gt; result = forkJoinPool.submit(task);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Long</span> <span class="variable">res</span> <span class="operator">=</span> result.get(); <span class="comment">//调用任务的 get()方法返回结果</span></span><br><span class="line">            System.out.println(<span class="string">&quot;计算数列结果为:&quot;</span> + res);</span><br><span class="line">        &#125;<span class="keyword">catch</span> (InterruptedException e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;<span class="keyword">catch</span> (ExecutionException e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//验证</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">s</span> <span class="operator">=</span> <span class="number">0L</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">long</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt;= <span class="number">200000</span> ; i++) &#123;</span><br><span class="line">            s += i;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(s);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></T></T></T></p>
<h1 id="保障线程安全的设计技术"><a href="#保障线程安全的设计技术" class="headerlink" title="保障线程安全的设计技术"></a><strong>保障线程安全的设计技术</strong></h1><p>从面向对象设计的角度出发介绍几种保障线程安全的设计技术，这些技术可以使得我们在不必借助锁的情况下保障线程安全，避免锁可能导致的问题及开销。</p>
<h2 id="Java-运行时存储空间"><a href="#Java-运行时存储空间" class="headerlink" title="Java 运行时存储空间"></a><strong>Java 运行时存储空间</strong></h2><p>Java运行时(Java runtime)空间可以分为栈区,堆区与方法区(非堆空间)</p>
<p>栈空间(Stack Space)为线程的执行准备一段固定大小的存储空间，每个线程都有独立的线程栈空间，创建线程时就为线程分配栈空间。在线程栈中每调用一个方法就给方法分配一个栈帧，栈帧用于存储方法的局部变量，返回值等私有数据，即局部变量存储在栈空间中， 基本类型变量也是存储在栈空间中，引用类型变量值也是存储在栈空间中，引用的对象存储在堆中。 由于线程栈是相互独立的，一个线程不能访问另外一个线程的栈空间，因此线程对局部变量以及只能通过当前线程的局部变量才能访问的对象进行的操作具有固定的线程安全性。</p>
<p>堆空间(Heap Space)用于存储对象，是在 JVM 启动时分配的一段可以动态扩容的内存空间。创建对象时，在堆空间中给对象分配存储空间，实例变量就是存储在堆空间中的，堆空间是多个线程之间可以共享的空间，因此实例变量可以被多个线程共享。多个线程同时操作实例变量可能存在线程安全问题。</p>
<p>非堆空间(Non-Heap Space)用于存储常量，类的元数据等， 非堆空间也是在 JVM 启动时分配的一段可以动态扩容的存储空间。类的元数据包括静态变量，类有哪些方法及这些方法的元数据(方法名,参数,返回值等)。 非堆空间也是多个线程可以共享的， 因此访问非堆空间中的静态变量也可能存在线程安全问题</p>
<p>堆空间和非堆空间是线程可以共享的空间，即实例变量与静态变量是线程可以共享的，可能存在线程安全问题。 栈空间是线程私有的存储空间，局部变量存储在栈空间中，局部变量具有固定的线程安全性。</p>
<h3 id="无状态对象"><a href="#无状态对象" class="headerlink" title="无状态对象"></a><strong>无状态对象</strong></h3><p>对象就是数据及对数据操作的封装，对象所包含的数据称为对象的状态(State)，实例变量与静态变量称为状态变量。</p>
<p>如果一个类的同一个实例被多个线程共享并不会使这些线程存储共享的状态，那么该类的实例就称为无状态对象(Stateless Object)。 反之如果一个类的实例被多个线程共享会使这些线程存在共享状态，那么该类的实例称为有状态对象。实际上无状态对象就是不包含任何实例变量也不包含任何静态变量的对象(不包含数据)。</p>
<p>线程安全问题的前提是多个线程存在共享的数据，实现线程安全的一种办法就是避免在多个线程之间共享数据，使用无状态对象就是这种方法。</p>
<h3 id="不可变对象"><a href="#不可变对象" class="headerlink" title="不可变对象"></a><strong>不可变对象</strong></h3><p>不可变对象是指一经创建它的状态就保持不变的对象，不可变对象具有固有的线程安全性。当不可变对象现实实体的状态发生变化时，系统会创建一个新的不可变对象，就如 String字符串对象。一个不可变对象需要满足以下条件：</p>
<ol>
<li>类本身使用 final 修饰,防止通过创建子类来改变它的定义</li>
<li>所有的字段都是 final 修饰的,final 字段在创建对象时必须显示初始化,不能被修改</li>
<li>如果字段引用了其他状态可变的对象(集合,数组),则这些字段 必须是 private 私有的</li>
</ol>
<p>不可变对象主要的应用场景：</p>
<ol>
<li><p>被建模对象的状态变化不频繁</p>
</li>
<li><p>同时对一组相关数据进行写操作，可以应用不可变对象，既可以保障原子性也可以避免锁的使用</p>
</li>
<li><p>使用不可变对象作为安全可靠的Map键，HashMap键值对的存储位置与键的 hashCode()有关，如果键的内部状态发生了变化会导致键的哈希码不同，可能会影响键值对的存储位置。如果 HashMap 的键是一个不可变对象，则 hashCode()方法的返回值恒定，存储位置是固定的。</p>
</li>
</ol>
<h3 id="线程特有对象"><a href="#线程特有对象" class="headerlink" title="线程特有对象"></a><strong>线程特有对象</strong></h3><p>我们可以选择不共享非线程安全的对象，对于非线程安全的对象，每个线程都创建一个该对象的实例，各个线程访问各自创建的实例，一个线程不能访问另外一个线程创建的实例。 这种各个线程创建各自的实例，<strong>一个实例只能被一个线程访问的对象就称为线程特有对象</strong>。 线程特有对象既保障了对非线程安全对象的访问的线程安全，又避免了锁的开销。线程特有对象也具有固有的线程安全性。</p>
<p>ThreadLocal<T>类相当于线程访问其特有对象的代理，即各个线程通过 ThreadLocal 对象可以创建并访问各自的线程特有对象，泛型 T 指定了线程特有对象的类型。一个线程可以使用不同的 ThreadLocal 实例来创建并访问不同的线程特有对象。</T></p>
<img src="/wang-cheng/2022/04/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B/ThreadLocal.png" class title="ThreadLocal">
<p>ThreadLocal 实例为每个访问它的线程都关联了一个该线程特有的 对象, ThreadLocal 实例都有当前线程与特有实例之间的一个关联。</p>
<h1 id="锁的优化及注意事项"><a href="#锁的优化及注意事项" class="headerlink" title="锁的优化及注意事项"></a><strong>锁的优化及注意事项</strong></h1><h2 id="有助于提高锁性能的几点建议"><a href="#有助于提高锁性能的几点建议" class="headerlink" title="有助于提高锁性能的几点建议"></a><strong>有助于提高锁性能的几点建议</strong></h2><h3 id="减少锁持有时间"><a href="#减少锁持有时间" class="headerlink" title="减少锁持有时间"></a><strong>减少锁持有时间</strong></h3><p>对于使用锁进行并发控制的应用程序来说,如果单个线程特有锁 的时间过长,会导致锁的竞争更加激烈,会影响系统的性能.在程序中 需要尽可能减少线程对锁的持有时间,如下面代码:<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">public synchronized void <span class="built_in">syncMethod</span>()&#123; </span><br><span class="line">    <span class="built_in">othercode1</span>(); <span class="built_in">mutexMethod</span>(); <span class="built_in">othercode</span>(); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>在syncMethod同步方法中,假设只有mutexMethod()方法是需要同步的，othercode1()方法与 othercode2()方法不需要进行同步。 如果othercode1 与 othercode2 这两个方法需要花费较长的 CPU 时间,在并发量较大的情况下,这种同步方案会导致等待线程的大量增加。 一个 较好的优化方案是,只在必要时进行同步,可以减少锁的持有时间,提高系统的吞吐量,如把上面的代码改为：<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">public void <span class="built_in">syncMethod</span>()&#123; </span><br><span class="line">    <span class="built_in">othercode1</span>(); </span><br><span class="line">    synchronized (this) &#123; </span><br><span class="line">        <span class="built_in">mutexMethod</span>(); </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">othercode</span>(); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>只对 mutexMethod()方法进行同步，这种减少锁持有时间有助于降低锁冲突的可能性，提升系统的并发能力。</p>
<h3 id="减小锁的粒度"><a href="#减小锁的粒度" class="headerlink" title="减小锁的粒度"></a><strong>减小锁的粒度</strong></h3><p>一个锁保护的共享数据的数量大小称为锁的粒度。 如果一个锁保护的共享数据的数量大就称该锁的粒度粗，否则称该锁的粒度细。锁的粒度过粗会导致线程在申请锁时需要进行不必要的等待。减少锁粒度是一种削弱多线程锁竞争的一种手段，可以提高系统的并发性。</p>
<p>在JDK7前，java.util.concurrent.ConcurrentHashMap类采用分段锁协议，可以提高程序的并发性。</p>
<h3 id="使用读写分离锁代替独占锁"><a href="#使用读写分离锁代替独占锁" class="headerlink" title="使用读写分离锁代替独占锁"></a><strong>使用读写分离锁代替独占锁</strong></h3><p>使用ReadWriteLock读写分离锁可以提高系统性能，使用读写分离锁也是减小锁粒度的一种特殊情况。第二条建议是能分割数据结构实现减小锁的粒度，那么读写锁是对系统功能点的分割。</p>
<p>在多数情况下都允许多个线程同时读，在写的使用采用独占锁，在读多写少的情况下，使用读写锁可以大大提高系统的并发能力。</p>
<h3 id="锁分离"><a href="#锁分离" class="headerlink" title="锁分离"></a><strong>锁分离</strong></h3><p>将读写锁的思想进一步延伸就是锁分离。读写锁是根据读写操作功能上的不同进行了锁分离。根据应用程序功能的特点，也可以对独占锁进行分离.如 java.util.concurrent.LinkedBlockingQueue 类中take()与put()方法分别从队头取数据,把数据添加到队尾。 虽然这两个方法都是对队列进行修改操作，由于操作的主体是链表，take()操作的是链表 的头部，put()操作的是链表的尾部，两者并不冲突。如果采用独占锁的话，这两个操作不能同时并发，在该类中就采用锁分离，take()取数据时有取锁， put()添加数据时有自己的添加锁，这样 take()与 put()相互独立实现了并发。</p>
<h3 id="粗锁化"><a href="#粗锁化" class="headerlink" title="粗锁化"></a><strong>粗锁化</strong></h3><p>为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短。但是凡事都有一个度，如果对同一个锁不断的进行请求，同步和释放，也会消耗系统资源.如：</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">method1</span>(<span class="params"></span>)&#123; </span><br><span class="line">    <span class="title function_">synchronized</span>(<span class="params"> lock </span>)&#123; </span><br><span class="line">        同步代码块 <span class="number">1</span> </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="title function_">synchronized</span>(<span class="params"> lock </span>)&#123;</span><br><span class="line">        同步代码块 <span class="number">2</span> </span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>JVM 在遇到一连串不断对同一个锁进行请求和释放操作时，会把所有的锁整合成对锁的一次请求，从而减少对锁的请求次数，这个操作叫锁的粗化，如上一段代码会整合为：<br><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">method1</span>(<span class="params"></span>)&#123; </span><br><span class="line">    <span class="title function_">synchronized</span>(<span class="params"> lock </span>)&#123; </span><br><span class="line">        同步代码块 <span class="number">1</span> </span><br><span class="line">        同步代码块 <span class="number">2</span> </span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>在开发过程中，也应该有意识的在合理的场合进行锁的粗化，尤其在循环体内请求锁时，如：<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">for</span>(int i = <span class="number">0</span> ; i&lt; <span class="number">100</span>; i++)&#123; </span><br><span class="line">    <span class="built_in">synchronized</span>(lock)&#123;</span><br><span class="line"></span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>这种情况下，意味着每次循环都需要申请锁和释放锁，所以一种更合理的做法就是在循环外请求一次锁，如：<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">synchronized</span>( lock )&#123; </span><br><span class="line">    <span class="built_in">for</span>(int i = <span class="number">0</span> ; i&lt; <span class="number">100</span>; i++)&#123;&#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="JVM-对锁的优化"><a href="#JVM-对锁的优化" class="headerlink" title="JVM 对锁的优化"></a><strong>JVM 对锁的优化</strong></h2><h3 id="锁偏向"><a href="#锁偏向" class="headerlink" title="锁偏向"></a><strong>锁偏向</strong></h3><p>锁偏向是一种针对加锁操作的优化，如果一个线程获得了锁，那么锁就进入偏向模式，当这个线程再次请求锁时，无须再做任何同步操作，这样可以节省有关锁申请的时间，提高了程序的性能。</p>
<p>锁偏向在没有锁竞争的场合可以有较好的优化效果，对于锁竞争比较激烈的场景，效果不佳， 锁竞争激烈的情况下可能是每次都是不同的线程来请求锁，这时偏向模式失效。</p>
<h3 id="轻量级锁"><a href="#轻量级锁" class="headerlink" title="轻量级锁"></a><strong>轻量级锁</strong></h3><p>如果锁偏向失败,JVM 不会立即挂起线程，还会使用一种称为轻量级锁的优化手段.会将对象的头部作为指针，指向持有锁的线程堆栈内部， 来判断一个线程是否持有对象锁。 如果线程获得轻量级锁成功， 就进入临界区。 如果获得轻量级锁失败，表示其他线程抢到了锁，那么当前线程的锁的请求就膨胀为重量级锁。当前线程就转到阻塞队列中变为阻塞状态。</p>
<p>偏向锁，轻量级锁都是乐观锁，重量级锁是悲观锁。</p>
<p>一个对象刚开始实例化时,没有任何线程访问它,它是可偏向的,即它认为只可能有一个线程来访问它,所以当第一个线程来访问它的时候,它会偏向这个线程. 偏向第一个线程,这个线程在修改对象头成为偏向锁时使用 CAS 操作,将对象头中 ThreadId 改成自己的 ID,之后再访问这个对象时,只需要对比 ID 即可. 一旦有第二个线程访问该对象,因为偏向锁不会主动释放,所以第二个线程可以查看对象的偏向状态,当第二个线程访问对象时,表示在这个对象上已经存在竞争了,检查原来持有对象锁的线程是否存活,如果挂了则将对象变为无锁状态,然后重新偏向新的线程; 如果原来的线程依然存活,则马上执行原来线程的栈,检查该对象的使用情况,如果仍然需要偏向锁,则偏向锁升级为轻量级锁.</p>
<p>轻量级锁认为竞争存在,但是竞争的程度很轻,一般两个线程对同一个锁的操作会错开,或者稍微等待一下(自旋)另外一个线程就会释放锁. 当自旋超过一定次数,或者一个线程持有锁,一个线程在自旋,又来第三个线程访问时, 轻量级锁会膨胀为重量级锁, 重量级锁除了持有锁的线程外,其他的线程都阻塞.</p>
<hr>
<p>CAS、乐观锁、悲观锁</p>
<p>悲观锁：当一个线程去获取锁之后，另一个线程来获取锁会阻塞等待，常见的synchronized就是悲观锁</p>
<p>乐观锁：当一个线程去操作的时候，其他的线程也可以操作，操作提交是根据版本控制的，CAS就是乐观锁，通过比较预期值与实际值的比较考虑是否提交更新。悲观锁适合写场景多的情况，乐观锁适合读场景多的情况</p>
<p>公平锁：所有线程都能公平的获取到锁，线程申请锁之后会排队到一个线程组里，根据申请顺序去获取锁的使用</p>
<p>非公平锁：不是所有线程都能拿到锁，当一个线程去请求的时候可能直接插队拿到了锁，有一些线程可能会饿死，常用的synchronized、ReentrantLock</p>
<p>可重入锁：在使用锁之后还可以申请获取锁</p>
<p>不可重入锁：在获取锁之后不可在内部重新获取锁</p>
<p>小结：可重入锁能一定程度的避免死锁 synchronized、ReentrantLock 重入锁</p>
<p>自旋锁：在获取锁的时候如果有其他线程占用锁，那么该线程就会一直循环去获取锁，一直到获取到为止</p>
<p>共享锁：一个线程占用锁之后，其他线程可以读数据，但不能修改\新增\删除数据</p>
<p>互斥锁：一个线程占用锁之后，其他线程不可以进行任何操作</p>
<p>死锁：两个或两个以上的线程在执行过程中，由于竞争资源或者由于彼此通信而造成的一种阻塞的现象，若无外力作用，它们都将无法让程序进行下去</p>
<hr>

    </div>

    
    
    

      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/wang-cheng/2022/04/05/%E7%AE%97%E6%B3%95%E7%AC%94%E8%AE%B0-%E4%BA%94/" rel="prev" title="算法笔记(五)">
      <i class="fa fa-chevron-left"></i> 算法笔记(五)
    </a></div>
      <div class="post-nav-item">
    <a href="/wang-cheng/2022/04/19/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%88%B7%E9%A2%98/" rel="next" title="数据库刷题">
      数据库刷题 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95"><span class="nav-number">1.</span> <span class="nav-text">线程的常用方法</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B%E7%9A%84%E4%BC%98%E5%8A%BF%E4%B8%8E%E5%AD%98%E5%9C%A8%E7%9A%84%E9%A3%8E%E9%99%A9"><span class="nav-number">2.</span> <span class="nav-text">多线程编程的优势与存在的风险</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98"><span class="nav-number">3.</span> <span class="nav-text">线程安全问题</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8E%9F%E5%AD%90%E6%80%A7"><span class="nav-number">3.1.</span> <span class="nav-text">原子性</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8F%AF%E8%A7%81%E6%80%A7"><span class="nav-number">3.2.</span> <span class="nav-text">可见性</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%9C%89%E5%BA%8F%E6%80%A7"><span class="nav-number">3.3.</span> <span class="nav-text">有序性</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%87%8D%E6%8E%92%E5%BA%8F"><span class="nav-number">3.3.1.</span> <span class="nav-text">重排序</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%8C%87%E4%BB%A4%E9%87%8D%E6%8E%92%E5%BA%8F"><span class="nav-number">3.3.2.</span> <span class="nav-text">指令重排序</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BF%9D%E8%AF%81%E5%86%85%E5%AD%98%E8%AE%BF%E9%97%AE%E7%9A%84%E9%A1%BA%E5%BA%8F%E6%80%A7"><span class="nav-number">3.3.3.</span> <span class="nav-text">保证内存访问的顺序性</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5"><span class="nav-number">3.4.</span> <span class="nav-text">线程同步</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%94%81%E6%A6%82%E5%BF%B5"><span class="nav-number">3.5.</span> <span class="nav-text">锁概念</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%94%81%E7%9A%84%E4%BD%9C%E7%94%A8"><span class="nav-number">3.6.</span> <span class="nav-text">锁的作用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%94%81%E7%9A%84%E7%9B%B8%E5%85%B3%E6%A6%82%E5%BF%B5"><span class="nav-number">3.7.</span> <span class="nav-text">锁的相关概念</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%86%85%E9%83%A8%E9%94%81-synchronized-%E5%85%B3%E9%94%AE%E5%AD%97"><span class="nav-number">3.8.</span> <span class="nav-text">内部锁:synchronized 关键字</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#synchronized-%E5%90%8C%E6%AD%A5%E4%BB%A3%E7%A0%81%E5%9D%97"><span class="nav-number">3.8.1.</span> <span class="nav-text">synchronized 同步代码块</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%84%8F%E8%AF%BB"><span class="nav-number">3.9.</span> <span class="nav-text">脏读</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%AD%BB%E9%94%81"><span class="nav-number">3.10.</span> <span class="nav-text">死锁</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E8%BD%BB%E9%87%8F%E7%BA%A7%E5%90%8C%E6%AD%A5%E6%9C%BA%E5%88%B6-volative-%E5%85%B3%E9%94%AE%E5%AD%97"><span class="nav-number">4.</span> <span class="nav-text">轻量级同步机制:volative 关键字</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#volatile-%E7%9A%84%E4%BD%9C%E7%94%A8"><span class="nav-number">4.1.</span> <span class="nav-text">volatile 的作用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#volatile-%E9%9D%9E%E5%8E%9F%E5%AD%90%E7%89%B9%E6%80%A7"><span class="nav-number">4.2.</span> <span class="nav-text">volatile 非原子特性</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%B8%B8%E7%94%A8%E5%8E%9F%E5%AD%90%E7%B1%BB%E8%BF%9B%E8%A1%8C%E8%87%AA%E5%A2%9E%E8%87%AA%E5%87%8F%E6%93%8D%E4%BD%9C"><span class="nav-number">4.3.</span> <span class="nav-text">常用原子类进行自增自减操作</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5-1"><span class="nav-number">5.</span> <span class="nav-text">线程同步</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#CAS"><span class="nav-number">5.1.</span> <span class="nav-text">CAS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8E%9F%E5%AD%90%E5%8F%98%E9%87%8F%E7%B1%BB"><span class="nav-number">5.2.</span> <span class="nav-text">原子变量类</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E9%97%B4%E7%9A%84%E9%80%9A%E4%BF%A1"><span class="nav-number">6.</span> <span class="nav-text">线程间的通信</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%AD%89%E5%BE%85-%E9%80%9A%E7%9F%A5%E6%9C%BA%E5%88%B6"><span class="nav-number">6.1.</span> <span class="nav-text">等待&#x2F;通知机制</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E7%AD%89%E5%BE%85%E9%80%9A%E7%9F%A5%E6%9C%BA%E5%88%B6"><span class="nav-number">6.1.1.</span> <span class="nav-text">什么是等待通知机制</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%AD%89%E5%BE%85-%E9%80%9A%E7%9F%A5%E6%9C%BA%E5%88%B6%E7%9A%84%E5%AE%9E%E7%8E%B0"><span class="nav-number">6.1.2.</span> <span class="nav-text">等待&#x2F;通知机制的实现</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#notify-%E4%B8%8E-notifyAll"><span class="nav-number">6.1.3.</span> <span class="nav-text">notify()与 notifyAll()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#wait-long-%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="nav-number">6.1.4.</span> <span class="nav-text">wait(long)的使用</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ThreadLocal-%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="nav-number">6.2.</span> <span class="nav-text">ThreadLocal 的使用</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#LOCK%E6%98%BE%E7%A4%BA%E9%94%81"><span class="nav-number">7.</span> <span class="nav-text">LOCK显示锁</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%94%81%E7%9A%84%E5%8F%AF%E9%87%8D%E5%85%A5%E6%80%A7"><span class="nav-number">7.1.</span> <span class="nav-text">锁的可重入性</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ReentrantLock"><span class="nav-number">7.2.</span> <span class="nav-text">ReentrantLock</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#ReentrantLock-%E9%94%81%E7%9A%84%E5%8F%AF%E9%87%8D%E5%85%A5%E6%80%A7"><span class="nav-number">7.2.1.</span> <span class="nav-text">ReentrantLock 锁的可重入性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#tryLock-%E6%96%B9%E6%B3%95"><span class="nav-number">7.2.2.</span> <span class="nav-text">tryLock()方法</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#newCondition-%E6%96%B9%E6%B3%95"><span class="nav-number">7.3.</span> <span class="nav-text">newCondition()方法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%85%AC%E5%B9%B3%E9%94%81%E4%B8%8E%E9%9D%9E%E5%85%AC%E5%B9%B3%E9%94%81"><span class="nav-number">7.4.</span> <span class="nav-text">公平锁与非公平锁</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#ReentrantReadWriteLock-%E8%AF%BB%E5%86%99%E9%94%81"><span class="nav-number">7.4.1.</span> <span class="nav-text">ReentrantReadWriteLock 读写锁</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E7%AE%A1%E7%90%86"><span class="nav-number">8.</span> <span class="nav-text">线程管理</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E7%BB%84"><span class="nav-number">8.1.</span> <span class="nav-text">线程组</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%8D%95%E8%8E%B7%E7%BA%BF%E7%A8%8B%E7%9A%84%E6%89%A7%E8%A1%8C%E5%BC%82%E5%B8%B8"><span class="nav-number">8.2.</span> <span class="nav-text">捕获线程的执行异常</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="nav-number">9.</span> <span class="nav-text">线程池</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="nav-number">9.1.</span> <span class="nav-text">什么是线程池</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#JDK-%E5%AF%B9%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%9A%84%E6%94%AF%E6%8C%81"><span class="nav-number">9.2.</span> <span class="nav-text">JDK 对线程池的支持</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%A0%B8%E5%BF%83%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%9A%84%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0"><span class="nav-number">9.2.1.</span> <span class="nav-text">核心线程池的底层实现</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ThreadFactory"><span class="nav-number">9.2.2.</span> <span class="nav-text">ThreadFactory</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%9B%91%E6%8E%A7%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="nav-number">9.2.3.</span> <span class="nav-text">监控线程池</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%89%A9%E5%B1%95%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="nav-number">9.2.4.</span> <span class="nav-text">扩展线程池</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BC%98%E5%8C%96%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%A4%A7%E5%B0%8F"><span class="nav-number">9.2.5.</span> <span class="nav-text">优化线程池大小</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E6%AD%BB%E9%94%81"><span class="nav-number">9.2.6.</span> <span class="nav-text">线程池死锁</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%B8%AD%E7%9A%84%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86"><span class="nav-number">9.2.7.</span> <span class="nav-text">线程池中的异常处理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ForkJoinPool-%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="nav-number">9.2.8.</span> <span class="nav-text">ForkJoinPool 线程池</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BF%9D%E9%9A%9C%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%8A%80%E6%9C%AF"><span class="nav-number">10.</span> <span class="nav-text">保障线程安全的设计技术</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Java-%E8%BF%90%E8%A1%8C%E6%97%B6%E5%AD%98%E5%82%A8%E7%A9%BA%E9%97%B4"><span class="nav-number">10.1.</span> <span class="nav-text">Java 运行时存储空间</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%97%A0%E7%8A%B6%E6%80%81%E5%AF%B9%E8%B1%A1"><span class="nav-number">10.1.1.</span> <span class="nav-text">无状态对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%B8%8D%E5%8F%AF%E5%8F%98%E5%AF%B9%E8%B1%A1"><span class="nav-number">10.1.2.</span> <span class="nav-text">不可变对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E7%89%B9%E6%9C%89%E5%AF%B9%E8%B1%A1"><span class="nav-number">10.1.3.</span> <span class="nav-text">线程特有对象</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E9%94%81%E7%9A%84%E4%BC%98%E5%8C%96%E5%8F%8A%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9"><span class="nav-number">11.</span> <span class="nav-text">锁的优化及注意事项</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%9C%89%E5%8A%A9%E4%BA%8E%E6%8F%90%E9%AB%98%E9%94%81%E6%80%A7%E8%83%BD%E7%9A%84%E5%87%A0%E7%82%B9%E5%BB%BA%E8%AE%AE"><span class="nav-number">11.1.</span> <span class="nav-text">有助于提高锁性能的几点建议</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%87%8F%E5%B0%91%E9%94%81%E6%8C%81%E6%9C%89%E6%97%B6%E9%97%B4"><span class="nav-number">11.1.1.</span> <span class="nav-text">减少锁持有时间</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%87%8F%E5%B0%8F%E9%94%81%E7%9A%84%E7%B2%92%E5%BA%A6"><span class="nav-number">11.1.2.</span> <span class="nav-text">减小锁的粒度</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BD%BF%E7%94%A8%E8%AF%BB%E5%86%99%E5%88%86%E7%A6%BB%E9%94%81%E4%BB%A3%E6%9B%BF%E7%8B%AC%E5%8D%A0%E9%94%81"><span class="nav-number">11.1.3.</span> <span class="nav-text">使用读写分离锁代替独占锁</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%94%81%E5%88%86%E7%A6%BB"><span class="nav-number">11.1.4.</span> <span class="nav-text">锁分离</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B2%97%E9%94%81%E5%8C%96"><span class="nav-number">11.1.5.</span> <span class="nav-text">粗锁化</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#JVM-%E5%AF%B9%E9%94%81%E7%9A%84%E4%BC%98%E5%8C%96"><span class="nav-number">11.2.</span> <span class="nav-text">JVM 对锁的优化</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%94%81%E5%81%8F%E5%90%91"><span class="nav-number">11.2.1.</span> <span class="nav-text">锁偏向</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%94%81"><span class="nav-number">11.2.2.</span> <span class="nav-text">轻量级锁</span></a></li></ol></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">王诚</p>
  <div class="site-description" itemprop="description">命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/wang-cheng/archives/">
        
          <span class="site-state-item-count">32</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/wang-cheng/tags/">
          
        <span class="site-state-item-count">12</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">王诚</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

        
<div class="busuanzi-count">
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <span class="post-meta-item" id="busuanzi_container_site_uv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-user"></i>
      </span>
      <span class="site-uv" title="总访客量">
        <span id="busuanzi_value_site_uv"></span>
      </span>
    </span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item" id="busuanzi_container_site_pv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-eye"></i>
      </span>
      <span class="site-pv" title="总访问量">
        <span id="busuanzi_value_site_pv"></span>
      </span>
    </span>
</div>








      </div>
    </footer>
  </div>

  
  <script src="/wang-cheng/lib/anime.min.js"></script>
  <script src="/wang-cheng/lib/velocity/velocity.min.js"></script>
  <script src="/wang-cheng/lib/velocity/velocity.ui.min.js"></script>

<script src="/wang-cheng/js/utils.js"></script>

<script src="/wang-cheng/js/motion.js"></script>


<script src="/wang-cheng/js/schemes/pisces.js"></script>


<script src="/wang-cheng/js/next-boot.js"></script>




  















  

  

  

</body>
</html>
